]> git.sur5r.net Git - openldap/blob - clients/ud/auth.c
s/<stdlib.h>/<ac/stdlib.h>/
[openldap] / clients / ud / auth.c
1 /*
2  * Copyright (c) 1991, 1992 Regents of the University of Michigan.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of Michigan at Ann Arbor. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18
19 #include <ac/ctype.h>
20 #include <ac/krb.h>
21 #include <ac/string.h>
22 #include <ac/time.h>
23 #include <ac/unistd.h>
24
25 #ifdef HAVE_PWD_H
26 #include <pwd.h>
27 #endif
28
29 #include <lber.h>
30 #include <ldap.h>
31 #include <ldapconfig.h>
32
33 #include "ud.h"
34
35 #ifdef HAVE_KERBEROS
36 static char tktpath[20];        /* ticket file path */
37 static int kinit();
38 static int valid_tgt();
39 #endif
40
41 static void set_bound_dn(char *s);
42
43
44 int
45 auth( char *who, int implicit )
46 {
47         int rc;                 /* return code from ldap_bind() */
48         char *passwd = NULL;    /* returned by mygetpass() */
49         char **rdns;            /* for fiddling with the DN */
50         int authmethod;
51         int name_provided;      /* was a name passed in? */
52 #ifdef HAVE_GETPWUID
53         struct passwd *pw;      /* for getting user id */
54 #else
55         char *user;
56 #endif
57         char uidname[20];
58 #ifdef HAVE_KERBEROS
59         char **krbnames;        /* for kerberos names */
60         int kinited, ikrb;
61         char buf[5];
62         extern int krb_debug;
63 #endif
64         LDAPMessage *mp;        /* returned from find() */
65         static char prompt[MED_BUF_SIZE];       /* place for us to sprintf the prompt */
66         static char name[MED_BUF_SIZE]; /* place to store the user's name */
67         static char password[MED_BUF_SIZE];     /* password entered by user */
68
69 #ifdef DEBUG
70         if (debug & D_TRACE)
71                 fprintf(stderr, "auth(%s, NULL)\n", who);
72 #endif
73         name_provided = ( who != NULL );
74
75         /*
76          *  The user needs to bind.  If <who> is not specified, we
77          *  assume that authenticating as user id is what user wants.
78          */
79         if (who == NULL && implicit) {
80                 uidname[0] = '\0';
81
82 #ifdef HAVE_GETPWUID
83                 if ((pw = getpwuid((uid_t)geteuid())) != (struct passwd *) NULL) {
84                         sprintf(uidname, "uid=%s", pw->pw_name);
85                 }
86 #else
87                 user = getenv("USER");
88                 if(user == NULL) user = getenv("USERNAME");
89                 if(user == NULL) user = getenv("LOGNAME");
90
91                 if(user != NULL) {
92                         sprintf(uidname, "uid=%s", user);
93                 }
94 #endif
95
96                 if(uidname[0] != '\0') {
97                         who = uidname;
98                 }
99         }
100
101         if ( who == NULL ) {
102                 if ( implicit )
103                         printf( "You must first authenticate yourself to the Directory.\n" );
104 #ifdef UOFM
105                 printf("  What is your name or uniqname? ");
106 #else
107                 printf("  What is your name or user id? ");
108 #endif
109                 fflush(stdout);
110                 fetch_buffer(name, sizeof(name), stdin);
111                 if (name[0] == '\0')
112                         return( -1 );
113                 who = name;
114         }
115
116 #ifdef DEBUG
117         if (debug & D_AUTHENTICAT)
118                 printf("  Authenticating as \"%s\"\n", who);
119 #endif
120
121         /*
122          *  Bail out if the name is bogus.  If not, strip off the junk
123          *  at the start of the DN, build a prompt, and get a password 
124          *  from the user.  Then perform the ldap_bind().
125          */
126         if ((mp = find(who, TRUE)) == NULL) {
127                 printf("  I could not find \"%s\" in the Directory.\n", who);
128                 printf("  I used a search base of ");
129                 printbase("", search_base);
130                 printf("\n");
131 #ifdef DEBUG
132                 if (debug & D_AUTHENTICAT)
133                         printf("  Could not find \"%s\"\n", who);
134 #endif
135                 return(-1);
136         }
137
138         /*
139          *  Fill in the Entry structure.  May be handy later.
140          */
141         (void) parse_answer(mp);
142
143         rdns = ldap_explode_dn(Entry.DN, TRUE);
144         printf("  Authenticating to the directory as \"%s\"...\n", *rdns );
145
146 #ifdef HAVE_KERBEROS
147         /*
148          * First, if the user has a choice of auth methods, ask which
149          * one they want to use.  if they want kerberos, ask which
150          * krbname they want to bind as.
151          */
152
153         if ( (krbnames = ldap_get_values( ld, mp, "krbName" )) != NULL ) {
154                 int     choice, hassimple;
155
156                 hassimple = (ldap_compare_s( ld, Entry.DN, 
157                                 "userPassword", "x" ) == LDAP_COMPARE_FALSE);
158                 (void) ldap_msgfree(mp);
159
160                 /* if we're running as a server (e.g., out of inetd) */
161                 if ( ! isatty( 1 ) ) {
162                         strcpy( tktpath, "/tmp/ud_tktXXXXXX" );
163                         mktemp( tktpath );
164                         krb_set_tkt_string( tktpath );
165                 }
166
167                 kinited = valid_tgt( krbnames );
168
169                 if ( hassimple && !kinited ) {
170                         printf("  Which password would you like to use?\n");
171                         printf("    1 -> LDAP password\n");
172 #ifdef UOFM
173                         printf("    2 -> UMICH password (aka Uniqname or Kerberos password)\n");
174 #else
175                         printf("    2 -> Kerberos password\n");
176 #endif
177
178                         do {
179                                 printf("  Enter 1 or 2: ");
180                                 fflush(stdout);
181
182                                 fetch_buffer(buf, sizeof(buf), stdin);
183                                 choice = atoi(buf);
184                         } while (choice != 1 && choice != 2);
185
186                         authmethod = (choice == 1 ? LDAP_AUTH_SIMPLE :
187                             LDAP_AUTH_KRBV4);
188                 } else {
189                         authmethod = LDAP_AUTH_KRBV4;
190                 }
191         } else {
192                 authmethod = LDAP_AUTH_SIMPLE;
193                 (void) ldap_msgfree(mp);
194         }
195
196         /*
197          * if they are already kinited, we don't need to ask for a 
198          * password.
199          */
200
201         if ( authmethod == LDAP_AUTH_KRBV4 ) {
202                 if ( ! kinited ) {
203                         if ( krbnames[1] != NULL ) {
204                                 int     i;
205
206                                 /* ask which one to use */
207 #ifdef UOFM
208                                 printf("  Which UMICH (aka Kerberos or uniqname) name would you like to use?\n");
209 #else
210                                 printf("  Which Kerberos name would you like to use?\n");
211 #endif
212                                 for ( i = 0; krbnames[i] != NULL; i++ ) {
213                                         printf( "    %d -> %s\n", i + 1,
214                                             krbnames[i] );
215                                 }
216                                 do {
217                                         printf("  Enter a number between 1 and %d: ", i );
218                                         fflush( stdout );
219
220                                         fetch_buffer(buf, sizeof(buf), stdin);
221                                         ikrb = atoi(buf) - 1;
222                                 } while ( ikrb > i - 1 || ikrb < 0 );
223                         } else {
224                                 ikrb = 0;
225                         }
226
227                         /* kinit */
228                         if ( kinit( krbnames[ikrb] ) != 0 ) {
229                                 (void) ldap_value_free(rdns);
230                                 (void) ldap_value_free(krbnames);
231                                 return(-1);
232                         }
233                 }
234         } else {
235 #endif
236                 authmethod = LDAP_AUTH_SIMPLE;
237                 sprintf(prompt, "  Enter your LDAP password: ");
238                 do {
239                         passwd = mygetpass(prompt);
240                 } while (passwd != NULL && *passwd == '\0');
241                 if (passwd == NULL) {
242                         (void) ldap_value_free(rdns);
243                         return(0);
244                 }
245 #ifdef HAVE_KERBEROS
246         }
247         (void) ldap_value_free(krbnames);
248 #endif
249         ldap_flush_cache( ld );
250         rc = ldap_bind_s(ld, Entry.DN, passwd, authmethod);
251         if (rc != LDAP_SUCCESS) {
252                 int ld_errno;
253                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
254                 if (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)
255                         fprintf(stderr, "  Entry has no password\n");
256                 else if (ld_errno == LDAP_INVALID_CREDENTIALS)
257 #ifdef HAVE_KERBEROS
258                         if ( authmethod == LDAP_AUTH_KRBV4 ) {
259                                 fprintf(stderr, "  The Kerberos credentials are invalid.\n");
260                         } else {
261 #endif
262                                 fprintf(stderr, "  The password you provided is incorrect.\n");
263 #ifdef HAVE_KERBEROS
264                         }
265 #endif
266                 else
267                         ldap_perror(ld, "ldap_bind_s" );
268                 (void) ldap_bind_s(ld, default_bind_object,
269                          (char *) NULL, LDAP_AUTH_SIMPLE);
270                 if (default_bind_object == NULL)
271                         set_bound_dn(NULL);
272                 else
273                         set_bound_dn(default_bind_object);
274                 bind_status = UD_NOT_BOUND;
275                 if (verbose)
276                         printf("  Authentication failed.\n\n");
277                 (void) ldap_value_free(rdns);
278                 return(-1);
279         }
280         else if (verbose)
281                 printf("  Authentication successful.\n\n");
282         else
283                 printf("\n");
284         set_bound_dn(Entry.DN);
285         bind_status = UD_BOUND;
286         if (passwd != NULL)
287                 (void) strcpy(password, passwd);
288         (void) ldap_value_free(rdns);
289         return(0);
290 }
291
292 #ifdef HAVE_KERBEROS
293
294 #define FIVEMINS        ( 5 * 60 )
295 #define TGT             "krbtgt"
296
297 static void
298 str2upper( char *s )
299 {
300         char    *p;
301
302         for ( p = s; *p != '\0'; ++p ) {
303                 *p = TOUPPER( (unsigned char) *p );
304         }
305 }
306
307
308 static int
309 valid_tgt( char **names )
310 {
311         int             i;
312         char            name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
313         CREDENTIALS     cred;
314
315         for ( i = 0; names[i] != NULL; i++ ) {
316                 if ( kname_parse( name, inst, realm, names[i] ) != KSUCCESS ) {
317                         fprintf( stderr, "Bad format for krbName %s\n",
318                             names[i] );
319                         fprintf( stderr, "Contact x500@umich.edu\n" );
320                         return( 0 );
321                 }
322
323 #ifdef HAVE_AFS_KERBEROS
324                 /*
325                  * realm must be uppercase for krb_ routines
326                  */
327                 str2upper( realm );
328 #endif /* HAVE_AFS_KERBEROS */
329
330                 /*
331                 * check ticket file for a valid ticket granting ticket
332                 * my check is: have ticket granting ticket and it is good for
333                 * at least 5 more minutes
334                 */
335                 if ( krb_get_cred( TGT, realm, realm,
336                     &cred ) == KSUCCESS && time( 0 ) + FIVEMINS <
337                     cred.issue_date + (u_char)cred.lifetime * FIVEMINS ) {
338                         return( 1 );
339                 }
340         }
341
342         return( 0 );
343 }
344
345 static char *kauth_name;
346
347 /*ARGSUSED*/
348 int
349 krbgetpass( char *user, char *inst, char *realm, char *pw, C_Block key )
350 {
351         char    *p, lcrealm[ REALM_SZ ], prompt[256], *passwd;
352
353 #ifdef UOFM
354         sprintf(prompt, "  Enter the UMICH password (same as Uniqname or Kerberos password)\n  for %s: ", kauth_name );
355 #else
356         sprintf(prompt, "  Enter Kerberos password for %s: ", kauth_name );
357 #endif
358         do {
359                 passwd = mygetpass(prompt);
360         } while (passwd != NULL && *passwd == '\0');
361         if (passwd == NULL) {
362                 return(-1);
363         }
364
365 #ifdef HAVE_AFS_KERBEROS
366         strcpy( lcrealm, realm );
367         for ( p = lcrealm; *p != '\0'; ++p ) {
368                 *p = TOLOWER( (unsigned char) *p );
369         }
370
371         ka_StringToKey( passwd, lcrealm, key );
372 #else /* HAVE_AFS_KERBEROS */
373         string_to_key( passwd, key );
374 #endif /* HAVE_AFS_KERBEROS */
375
376         return( 0 );
377 }
378
379 static int
380 kinit( char *kname )
381 {
382         int     rc;
383         char    name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
384
385         kauth_name = kname;
386
387         if ( kname_parse( name, inst, realm, kname ) != KSUCCESS ) {
388                 fprintf( stderr, "Bad format for krbName %s\n",
389                     kname );
390                 fprintf( stderr, "Contact x500@umich.edu\n" );
391                 return( -1 );
392         }
393
394 #ifdef HAVE_AFS_KERBEROS
395         /*
396          * realm must be uppercase for krb_ routines
397          */
398         str2upper( realm );
399 #endif /* HAVE_AFS_KERBEROS */
400
401         rc = krb_get_in_tkt( name, inst, realm, TGT, realm,
402             DEFAULT_TKT_LIFE, krbgetpass, NULL, NULL );
403
404         if ( rc != KSUCCESS ) {
405                 switch ( rc ) {
406                 case SKDC_CANT:
407                         fprintf( stderr, "Can't contact Kerberos server for %s\n", realm );
408                         break;
409                 default:
410                         fprintf( stderr, "%s: %s\n", name, krb_err_txt[ rc ] );
411                         break;
412                 }
413                 return( -1 );
414         }
415
416         return( 0 );
417 }
418
419 void
420 destroy_tickets( void )
421 {
422         if ( *tktpath != '\0' ) {
423                 unlink( tktpath );
424         }
425 }
426 #endif
427
428 static void
429 set_bound_dn( char *s )
430 {
431         if (bound_dn != NULL)
432                 Free(bound_dn);
433         bound_dn = (s == NULL) ? NULL : strdup(s);
434 }