openldap/clients/ud/auth.c
Kurt Zeilenga b5494457d8 Remove extern declarations of library functions from source.c.
This could cause problems on odd systems.  The generic
  headers should be extended as needed to include necessary
  system headers or, if necessary, make explicit declarations.
Extended ac/string.h header to look for string.h/strings.h if
  STDC_HEADERS is not defined.  Also provide basic declarations for
  str*() functions.  This could cause problems on odd systems.
Extended ac/unistd.h header to define basic declaration for misc
  functions that might be missing from headers.   This includes
  externs for getenv(), getopt(), mktemp(), tempname().
Protect fax500.h from multiple inclusion.  Moved includes of
  system/generic headers back to source files.
Made mail500 helper functions static.
Fixed includes of ctype.h, signal.h, etc. to use generics.
lutil/tempname.c: was including stdlib.h twice, one should stdio.h.
Wrapped <sys/resource.h> with HAVE_SYS_RESOURCE_H.
lber/io.c/ber_get_next(): Changed noctets back to signed.
  Used with BerRead which expects signed int as second arg and
  returns signed int.
1998-11-16 05:07:27 +00:00

435 lines
9.9 KiB
C

/*
* Copyright (c) 1991, 1992 Regents of the University of Michigan.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and that due credit is given
* to the University of Michigan at Ann Arbor. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission. This software
* is provided ``as is'' without express or implied warranty.
*/
#include "portable.h"
#include <stdio.h>
#include <stdlib.h>
#include <ac/ctype.h>
#include <ac/krb.h>
#include <ac/string.h>
#include <ac/time.h>
#include <ac/unistd.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#include <lber.h>
#include <ldap.h>
#include <ldapconfig.h>
#include "ud.h"
#ifdef HAVE_KERBEROS
static char tktpath[20]; /* ticket file path */
static int kinit();
static int valid_tgt();
#endif
static void set_bound_dn(char *s);
int
auth( char *who, int implicit )
{
int rc; /* return code from ldap_bind() */
char *passwd = NULL; /* returned by mygetpass() */
char **rdns; /* for fiddling with the DN */
int authmethod;
int name_provided; /* was a name passed in? */
#ifdef HAVE_GETPWUID
struct passwd *pw; /* for getting user id */
#else
char *user;
#endif
char uidname[20];
#ifdef HAVE_KERBEROS
char **krbnames; /* for kerberos names */
int kinited, ikrb;
char buf[5];
extern int krb_debug;
#endif
LDAPMessage *mp; /* returned from find() */
static char prompt[MED_BUF_SIZE]; /* place for us to sprintf the prompt */
static char name[MED_BUF_SIZE]; /* place to store the user's name */
static char password[MED_BUF_SIZE]; /* password entered by user */
#ifdef DEBUG
if (debug & D_TRACE)
fprintf(stderr, "auth(%s, NULL)\n", who);
#endif
name_provided = ( who != NULL );
/*
* The user needs to bind. If <who> is not specified, we
* assume that authenticating as user id is what user wants.
*/
if (who == NULL && implicit) {
uidname[0] = '\0';
#ifdef HAVE_GETPWUID
if ((pw = getpwuid((uid_t)geteuid())) != (struct passwd *) NULL) {
sprintf(uidname, "uid=%s", pw->pw_name);
}
#else
user = getenv("USER");
if(user == NULL) user = getenv("USERNAME");
if(user == NULL) user = getenv("LOGNAME");
if(user != NULL) {
sprintf(uidname, "uid=%s", user);
}
#endif
if(uidname[0] != '\0') {
who = uidname;
}
}
if ( who == NULL ) {
if ( implicit )
printf( "You must first authenticate yourself to the Directory.\n" );
#ifdef UOFM
printf(" What is your name or uniqname? ");
#else
printf(" What is your name or user id? ");
#endif
fflush(stdout);
fetch_buffer(name, sizeof(name), stdin);
if (name[0] == '\0')
return( -1 );
who = name;
}
#ifdef DEBUG
if (debug & D_AUTHENTICAT)
printf(" Authenticating as \"%s\"\n", who);
#endif
/*
* Bail out if the name is bogus. If not, strip off the junk
* at the start of the DN, build a prompt, and get a password
* from the user. Then perform the ldap_bind().
*/
if ((mp = find(who, TRUE)) == NULL) {
(void) ldap_msgfree(mp);
printf(" I could not find \"%s\" in the Directory.\n", who);
printf(" I used a search base of ");
printbase("", search_base);
printf("\n");
#ifdef DEBUG
if (debug & D_AUTHENTICAT)
printf(" Could not find \"%s\"\n", who);
#endif
return(-1);
}
/*
* Fill in the Entry structure. May be handy later.
*/
(void) parse_answer(mp);
rdns = ldap_explode_dn(Entry.DN, TRUE);
printf(" Authenticating to the directory as \"%s\"...\n", *rdns );
#ifdef HAVE_KERBEROS
/*
* First, if the user has a choice of auth methods, ask which
* one they want to use. if they want kerberos, ask which
* krbname they want to bind as.
*/
if ( (krbnames = ldap_get_values( ld, mp, "krbName" )) != NULL ) {
int choice, hassimple;
hassimple = (ldap_compare_s( ld, Entry.DN,
"userPassword", "x" ) == LDAP_COMPARE_FALSE);
(void) ldap_msgfree(mp);
/* if we're running as a server (e.g., out of inetd) */
if ( ! isatty( 1 ) ) {
strcpy( tktpath, "/tmp/ud_tktXXXXXX" );
mktemp( tktpath );
krb_set_tkt_string( tktpath );
}
kinited = valid_tgt( krbnames );
if ( hassimple && !kinited ) {
printf(" Which password would you like to use?\n");
printf(" 1 -> LDAP password\n");
#ifdef UOFM
printf(" 2 -> UMICH password (aka Uniqname or Kerberos password)\n");
#else
printf(" 2 -> Kerberos password\n");
#endif
do {
printf(" Enter 1 or 2: ");
fflush(stdout);
fetch_buffer(buf, sizeof(buf), stdin);
choice = atoi(buf);
} while (choice != 1 && choice != 2);
authmethod = (choice == 1 ? LDAP_AUTH_SIMPLE :
LDAP_AUTH_KRBV4);
} else {
authmethod = LDAP_AUTH_KRBV4;
}
} else {
authmethod = LDAP_AUTH_SIMPLE;
(void) ldap_msgfree(mp);
}
/*
* if they are already kinited, we don't need to ask for a
* password.
*/
if ( authmethod == LDAP_AUTH_KRBV4 ) {
if ( ! kinited ) {
if ( krbnames[1] != NULL ) {
int i;
/* ask which one to use */
#ifdef UOFM
printf(" Which UMICH (aka Kerberos or uniqname) name would you like to use?\n");
#else
printf(" Which Kerberos name would you like to use?\n");
#endif
for ( i = 0; krbnames[i] != NULL; i++ ) {
printf( " %d -> %s\n", i + 1,
krbnames[i] );
}
do {
printf(" Enter a number between 1 and %d: ", i );
fflush( stdout );
fetch_buffer(buf, sizeof(buf), stdin);
ikrb = atoi(buf) - 1;
} while ( ikrb > i - 1 || ikrb < 0 );
} else {
ikrb = 0;
}
/* kinit */
if ( kinit( krbnames[ikrb] ) != 0 ) {
(void) ldap_value_free(rdns);
(void) ldap_value_free(krbnames);
return(-1);
}
}
} else {
#endif
authmethod = LDAP_AUTH_SIMPLE;
sprintf(prompt, " Enter your LDAP password: ");
do {
passwd = mygetpass(prompt);
} while (passwd != NULL && *passwd == '\0');
if (passwd == NULL) {
(void) ldap_value_free(rdns);
return(0);
}
#ifdef HAVE_KERBEROS
}
(void) ldap_value_free(krbnames);
#endif
ldap_flush_cache( ld );
rc = ldap_bind_s(ld, Entry.DN, passwd, authmethod);
if (rc != LDAP_SUCCESS) {
int ld_errno;
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
if (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)
fprintf(stderr, " Entry has no password\n");
else if (ld_errno == LDAP_INVALID_CREDENTIALS)
#ifdef HAVE_KERBEROS
if ( authmethod == LDAP_AUTH_KRBV4 ) {
fprintf(stderr, " The Kerberos credentials are invalid.\n");
} else {
#endif
fprintf(stderr, " The password you provided is incorrect.\n");
#ifdef HAVE_KERBEROS
}
#endif
else
ldap_perror(ld, "ldap_bind_s" );
(void) ldap_bind_s(ld, default_bind_object,
(char *) NULL, LDAP_AUTH_SIMPLE);
if (default_bind_object == NULL)
set_bound_dn(NULL);
else
set_bound_dn(default_bind_object);
bind_status = UD_NOT_BOUND;
if (verbose)
printf(" Authentication failed.\n\n");
(void) ldap_value_free(rdns);
return(-1);
}
else if (verbose)
printf(" Authentication successful.\n\n");
else
printf("\n");
set_bound_dn(Entry.DN);
bind_status = UD_BOUND;
if (passwd != NULL)
(void) strcpy(password, passwd);
(void) ldap_value_free(rdns);
return(0);
}
#ifdef HAVE_KERBEROS
#define FIVEMINS ( 5 * 60 )
#define TGT "krbtgt"
static void
str2upper( char *s )
{
char *p;
for ( p = s; *p != '\0'; ++p ) {
*p = TOUPPER( *p );
}
}
static int
valid_tgt( char **names )
{
int i;
char name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
CREDENTIALS cred;
for ( i = 0; names[i] != NULL; i++ ) {
if ( kname_parse( name, inst, realm, names[i] ) != KSUCCESS ) {
fprintf( stderr, "Bad format for krbName %s\n",
names[i] );
fprintf( stderr, "Contact x500@umich.edu\n" );
return( 0 );
}
#ifdef HAVE_AFS_KERBEROS
/*
* realm must be uppercase for krb_ routines
*/
str2upper( realm );
#endif /* HAVE_AFS_KERBEROS */
/*
* check ticket file for a valid ticket granting ticket
* my check is: have ticket granting ticket and it is good for
* at least 5 more minutes
*/
if ( krb_get_cred( TGT, realm, realm,
&cred ) == KSUCCESS && time( 0 ) + FIVEMINS <
cred.issue_date + (u_char)cred.lifetime * FIVEMINS ) {
return( 1 );
}
}
return( 0 );
}
static char *kauth_name;
/*ARGSUSED*/
int
krbgetpass( char *user, char *inst, char *realm, char *pw, C_Block key )
{
char *p, lcrealm[ REALM_SZ ], prompt[256], *passwd;
#ifdef UOFM
sprintf(prompt, " Enter the UMICH password (same as Uniqname or Kerberos password)\n for %s: ", kauth_name );
#else
sprintf(prompt, " Enter Kerberos password for %s: ", kauth_name );
#endif
do {
passwd = mygetpass(prompt);
} while (passwd != NULL && *passwd == '\0');
if (passwd == NULL) {
return(-1);
}
#ifdef HAVE_AFS_KERBEROS
strcpy( lcrealm, realm );
for ( p = lcrealm; *p != '\0'; ++p ) {
*p = TOLOWER( *p );
}
ka_StringToKey( passwd, lcrealm, key );
#else /* HAVE_AFS_KERBEROS */
string_to_key( passwd, key );
#endif /* HAVE_AFS_KERBEROS */
return( 0 );
}
static int
kinit( char *kname )
{
int rc;
char name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
kauth_name = kname;
if ( kname_parse( name, inst, realm, kname ) != KSUCCESS ) {
fprintf( stderr, "Bad format for krbName %s\n",
kname );
fprintf( stderr, "Contact x500@umich.edu\n" );
return( -1 );
}
#ifdef HAVE_AFS_KERBEROS
/*
* realm must be uppercase for krb_ routines
*/
str2upper( realm );
#endif /* HAVE_AFS_KERBEROS */
rc = krb_get_in_tkt( name, inst, realm, TGT, realm,
DEFAULT_TKT_LIFE, krbgetpass, NULL, NULL );
if ( rc != KSUCCESS ) {
switch ( rc ) {
case SKDC_CANT:
fprintf( stderr, "Can't contact Kerberos server for %s\n", realm );
break;
default:
fprintf( stderr, "%s: %s\n", name, krb_err_txt[ rc ] );
break;
}
return( -1 );
}
return( 0 );
}
void
destroy_tickets( void )
{
if ( *tktpath != '\0' ) {
unlink( tktpath );
}
}
#endif
static void
set_bound_dn( char *s )
{
if (bound_dn != NULL)
Free(bound_dn);
bound_dn = (s == NULL) ? NULL : strdup(s);
}