pull-prototypes-to-head-20020821
[openafs.git] / src / uss / uss_kauth.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  *      Implementation of basic procedures for the AFS user account
12  *      facility.
13  */
14
15 /*
16  * --------------------- Required definitions ---------------------
17  */
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21 RCSID("$Header$");
22
23 #include "uss_kauth.h"          /*Module interface*/
24 #include "uss_common.h"         /*Common defs & operations*/
25 #include <errno.h>
26 #include <pwd.h>
27
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #else
31 #ifdef HAVE_STRINGS_H
32 #include <strings.h>
33 #endif
34 #endif
35
36 #include <afs/com_err.h>
37 #include <afs/kautils.h>        /*MAXKTCREALMLEN*/
38 #include <afs/kaport.h>         /* pack_long */
39
40 extern int errno;
41
42 #define uss_kauth_MAX_SIZE      2048
43
44 #undef USS_KAUTH_DB
45
46 /*
47  * ---------------------- Exported variables ----------------------
48  */
49 struct ubik_client *uconn_kauthP;               /*Ubik connections
50                                                   to AuthServers*/
51
52 /*
53  * ------------------------ Private globals -----------------------
54  */
55 static int initDone = 0;                        /*Module initialized?*/
56 static char CreatorInstance[MAXKTCNAMELEN];     /*Instance string*/
57 static char UserPrincipal[MAXKTCNAMELEN];       /*Parsed user principal*/
58 static char UserInstance[MAXKTCNAMELEN];        /*Parsed user instance*/
59 static char UserCell[MAXKTCREALMLEN];           /*Parsed user cell*/
60 int doUnlog = 0;
61
62 /*-----------------------------------------------------------------------
63  * EXPORTED uss_kauth_InitAccountCreator
64  *
65  * Environment:
66  *      The command line must have been parsed.
67  *
68  * Side Effects:
69  *      As advertised.
70  *-----------------------------------------------------------------------*/
71
72 afs_int32 uss_kauth_InitAccountCreator()
73
74 { /*uss_kauth_InitAccountCreator*/
75
76     char *name;
77     struct passwd *pw;
78     int dotPosition;
79
80     /*
81      * Set up the identity of the principal performing the account
82      * creation (uss_AccountCreator).  It's either the administrator
83      * name provided at the call or the identity of the caller as
84      * gleaned from the password info.
85      */
86     if (uss_Administrator[0] != '\0') {
87         name = uss_Administrator;
88     }
89     else {  /* Administrator name not passed in */
90         pw = getpwuid(getuid());
91         if (pw == 0) {
92             fprintf(stderr,
93                      "%s: Can't figure out your name from your user id.\n",
94                      uss_whoami);
95             return(1);
96         }
97         name = pw->pw_name;
98     }
99
100     /* Break the *name into principal and instance */
101     dotPosition = strcspn(name, ".");
102     if (dotPosition >= MAXKTCNAMELEN) {
103         fprintf(stderr, "Admin principal name too long.\n");
104         return(1);
105     }
106     strncpy(uss_AccountCreator, name, dotPosition);
107     uss_AccountCreator[dotPosition] = '\0';
108
109     name += dotPosition;
110     if (name[0] == '.') {
111         name++;
112         if (strlen(name) >= MAXKTCNAMELEN) {
113             fprintf(stderr, "Admin instance name too long.\n");
114             return(1);
115         }
116         strcpy(CreatorInstance, name);
117     }
118     else {
119         CreatorInstance[0] = '\0';
120     }
121
122 #ifdef USS_KAUTH_DB_INSTANCE
123     fprintf(stderr,
124             "%s: Starting CreatorInstance is '%s', %d bytes\n",
125             uss_whoami, CreatorInstance, strlen(CreatorInstance));
126 #endif /* USS_KAUTH_DB_INSTANCE */
127     
128     return(0);
129 }
130
131 /*-----------------------------------------------------------------------
132  * static InitThisModule
133  *
134  * Description:
135  *      Set up this module, namely set up all the client state for
136  *      dealing with the Volume Location Server(s), including
137  *      network connections.
138  *
139  * Arguments:
140  *      a_noAuthFlag : Do we need authentication?
141  *      a_confDir    : Configuration directory to use.
142  *      a_cellName   : Cell we want to talk to.
143  *
144  * Returns:
145  *      0 if everything went fine, or
146  *      lower-level error code otherwise.
147  *
148  * Environment:
149  *      This routine will only be called once.
150  *
151  * Side Effects:
152  *      As advertised.
153  *------------------------------------------------------------------------*/
154
155 int Pipe=0;
156 static char *getpipepass() {
157     static char gpbuf[BUFSIZ];
158     /* read a password from stdin, stop on \n or eof */
159     register int i, tc;
160     memset(gpbuf, 0, sizeof(gpbuf));
161     for(i=0; i<(sizeof(gpbuf)-1); i++) {
162         tc = fgetc(stdin);
163         if (tc == '\n' || tc == EOF) break;
164         gpbuf[i] = tc;
165     }
166     return gpbuf;
167 }
168
169
170 afs_int32 InitThisModule()
171
172 { /*InitThisModule*/
173
174     static char rn[] = "uss_kauth:InitThisModule";
175     register afs_int32 code;
176     char *name, prompt[2*MAXKTCNAMELEN+20];
177     char *reasonString, longPassBuff[1024], shortPassBuff[9];
178     struct ktc_encryptionKey key;
179     struct ktc_token token, tok;
180     struct ktc_principal Name;
181
182     /*
183      * Only call this routine once.
184      */
185     if (initDone)
186         return(0);
187
188
189     /*
190      * Pull out the caller's administrator token if they have one.
191      */
192     code = ka_GetAdminToken(0, 0, uss_Cell, 0, 10*60*60, &token, 0/*new*/);
193     if (code) {
194         if (Pipe) {
195             strncpy(longPassBuff, getpipepass(), sizeof(longPassBuff));
196         } else {
197             /*
198              * Nope, no admin tokens available.  Get the key based on the
199              * full password and try again.
200              */
201             sprintf(prompt, "Password for '%s", uss_AccountCreator);
202             if (CreatorInstance[0])
203                sprintf(prompt+strlen(prompt), ".%s", CreatorInstance);
204             strcat(prompt, "': ");
205             code =
206                 ka_UserReadPassword(prompt,               /*Prompt to use*/
207                                     longPassBuff,         /*Long pwd buffer*/
208                                     sizeof(longPassBuff), /*Size of above*/
209                                     &reasonString);
210             if (code) {
211                 com_err(uss_whoami, code, "while getting password ");
212 #ifdef USS_KAUTH_DB
213                 printf("%s: Error code from ka_UserReadPassword(): %d\n",
214                        rn, code);
215 #endif /* USS_KAUTH_DB */
216                 return(code);
217             }
218         }
219         ka_StringToKey(longPassBuff, uss_Cell, &key);
220         code = ka_GetAdminToken(uss_AccountCreator,
221                                 CreatorInstance,
222                                 uss_Cell,
223                                 &key,
224                                 24*60*60,
225                                 &token,
226                                 0 /*new*/);
227         if (code) {
228             if ((code == KABADREQUEST) && (strlen(longPassBuff) > 8)) {
229                 /*
230                  * The key we provided just doesn't work, yet we
231                  * suspect that since the password is greater than 8
232                  * chars, it might be the case that we really need
233                  * to truncate the password to generate the appropriate
234                  * key.
235                  */
236                 com_err(uss_whoami, code,
237                         "while getting administrator token (trying shortened password next...)");
238 #ifdef USS_KAUTH_DB
239                 printf("%s: Error code from ka_GetAdminToken: %d\n",
240                        rn, code);
241 #endif /* USS_KAUTH_DB */
242                 strncpy(shortPassBuff, longPassBuff, 8);
243                 shortPassBuff[8] = 0;
244                 ka_StringToKey(shortPassBuff, uss_Cell, &key);
245                 code = ka_GetAdminToken(uss_AccountCreator,
246                                         CreatorInstance,
247                                         uss_Cell,
248                                         &key,
249                                         24*60*60,
250                                         &token,
251                                         0 /*new*/);
252                 if (code) {
253                     com_err(uss_whoami, code,
254                             "while getting administrator token (possibly wrong password, or not an administrative account)");
255 #ifdef USS_KAUTH_DB
256                     printf("%s: Error code from ka_GetAdminToken: %d\n",
257                            rn, code);
258 #endif /* USS_KAUTH_DB */
259                     return(code);
260                 }
261                 else {
262                     /*
263                      * The silly administrator has a long password!  Tell
264                      * him or her off in a polite way.
265                      */
266                     printf("%s: Shortened password accepted by the Authentication Server\n", uss_whoami);
267                 }
268             } /*Try a shorter password*/
269             else {
270                 /*
271                  * We failed to get an admin token, but the password is
272                  * of a reasonable length, so we're just hosed.
273                  */
274                 com_err(uss_whoami, code,
275                         "while getting administrator token (possibly wrong password, or not an administrative account)");
276 #ifdef USS_KAUTH_DB
277                 printf("%s: Error code from ka_GetAdminToken: %d\n",
278                        rn, code);
279 #endif /* USS_KAUTH_DB */
280                 return(code);
281             } /*Even the shorter password didn't work*/
282         } /*Key from given password didn't work*/
283     } /*First attempt to get admin token failed*/
284     
285     /*
286      * At this point, we have acquired an administrator token.  Let's
287      * proceed to set up a connection to the AuthServer.
288      */
289 #ifdef USS_KAUTH_DB_INSTANCE
290     fprintf(stderr,
291             "%s: CreatorInstance after ka_GetAdminToken(): '%s', %d bytes\n",
292             rn, CreatorInstance, strlen(CreatorInstance));
293 #endif /* USS_KAUTH_DB_INSTANCE */
294
295     /*
296      * Set up the connection to the AuthServer read/write site.
297      */
298     code = ka_AuthServerConn(uss_Cell, KA_MAINTENANCE_SERVICE,
299                              &token, &uconn_kauthP);
300     if (code) {
301         com_err(uss_whoami, code,
302                 "while establishing Authentication Server connection");
303 #ifdef USS_KAUTH_DB
304         printf("%s: Error code from ka_AuthServerConn: %d\n",
305                rn, code);
306 #endif /* USS_KAUTH_DB */
307         return(code);
308     }
309
310     if (uss_Administrator[0]) {
311         /*
312          * We must check to see if we have local tokens for admin since he'll may do
313          * various pioctl or calls to protection server that require tokens. Remember
314          * to remove this tokens at the end of the program...
315          */
316         strcpy (Name.name, "afs");
317         Name.instance[0] = '\0';
318         strncpy (Name.cell, uss_Cell, sizeof(Name.cell));
319         if (code = ktc_GetToken (&Name, &token, sizeof(struct ktc_token), &tok)) {
320             code = ka_UserAuthenticateLife (0, uss_AccountCreator, CreatorInstance,
321                                             uss_Cell,longPassBuff,10*60*60,&reasonString);
322             if (!code)
323                 doUnlog = 1;
324         }
325     }
326     
327     /*
328      * Declare our success.
329      */
330     initDone = 1;
331     return(0);
332
333 } /*InitThisModule*/
334
335
336 /*-----------------------------------------------------------------------
337  * EXPORTED uss_kauth_AddUser
338  *
339  * Environment:
340  *      The uconn_kauthP variable may already be set to an AuthServer
341  *      connection.
342  *
343  * Side Effects:
344  *      As advertised.
345  *------------------------------------------------------------------------*/
346
347 afs_int32 uss_kauth_AddUser(a_user, a_passwd)
348     char *a_user;
349     char *a_passwd;
350
351 { /*uss_kauth_AddUser*/
352
353     static char rn[] = "uss_kauth_AddUser";     /*Routine name*/
354     struct ktc_encryptionKey key;
355     afs_int32 code;
356
357     if (uss_SkipKaserver) {
358         /*
359          * Don't talk to the kaserver; assume calls succeded and simply return.
360          * Amasingly people want to update it (most likely kerberos) themselves...
361          */
362         if (uss_verbose)
363             printf("[Skip Kaserver option - Adding of user %s in Authentication DB not done]\n",
364                    a_user);
365         return 0;
366     }
367
368
369     /*
370      * Make sure the module has been initialized before we start trying
371      * to talk to AuthServers.
372      */
373     if (!initDone) {
374         code = InitThisModule();
375         if (code)
376             exit(code);
377     }
378
379     /*
380      * Given the (unencrypted) password and cell, generate a key to
381      * pass to the AuthServer.
382      */
383     ka_StringToKey(a_passwd, uss_Cell, &key);
384
385     if (!uss_DryRun) {
386         if (uss_verbose)
387             fprintf(stderr, "Adding user '%s' to the Authentication DB\n",
388                     a_user);
389
390 #ifdef USS_KAUTH_DB_INSTANCE
391         fprintf(stderr,
392                 "%s: KAM_CreateUser: user='%s', CreatorInstance='%s', %d bytes\n",
393                 rn, a_user, CreatorInstance, strlen(CreatorInstance));
394 #endif /* USS_KAUTH_DB_INSTANCE */
395         code = ubik_Call(KAM_CreateUser,
396                          uconn_kauthP,
397                          0,
398                          a_user,
399                          UserInstance,   /*set by CheckUsername()*/
400                          key);
401         if (code) {
402             if (code == KAEXIST){
403                 if (uss_verbose)
404                     fprintf(stderr,
405                             "%s: Warning: User '%s' already in Authentication DB\n",
406                             uss_whoami, a_user);
407             }
408             else {
409                 com_err(uss_whoami, code,
410                         "while adding user '%s' to Authentication DB",
411                         a_user);
412 #ifdef USS_KAUTH_DB
413                 printf("%s: Error code from KAM_CreateUser: %d\n",
414                        rn, code);
415 #endif /* USS_KAUTH_DB */
416                 return(code);
417             }
418         } /*KAM_CreateUser failed*/
419     } /*Not a dry run*/
420     else
421         fprintf(stderr,
422                 "\t[Dry run - user '%s' NOT added to Authentication DB]\n",
423                 a_user);
424
425     return(0);
426
427 } /*uss_kauth_AddUser*/
428
429
430 /*-----------------------------------------------------------------------
431  * EXPORTED uss_kauth_DelUser
432  *
433  * Environment:
434  *      The uconn_kauthP variable may already be set to an AuthServer
435  *      connection.
436  *
437  * Side Effects:
438  *      As advertised.
439  *------------------------------------------------------------------------*/
440
441 afs_int32 uss_kauth_DelUser(a_user)
442     char *a_user;
443
444 { /*uss_kauth_DelUser*/
445
446     static char rn[] = "uss_kauth_DelUser";     /*Routine name*/
447     register afs_int32 code;                            /*Return code*/
448
449     if (uss_SkipKaserver) {
450         /*
451          * Don't talk to the kaserver; assume calls succeded and simply return.
452          * Amasingly people want to update it (most likely kerberos) themselves...
453          */
454         if (uss_verbose)
455             printf("[Skip Kaserver option - Deleting of user %s in Authentication DB not done]\n",
456                    a_user);
457         return 0;
458     }
459
460     /*
461      * Make sure the module has been initialized before we start trying
462      * to talk to AuthServers.
463      */
464     if (!initDone) {
465         code = InitThisModule();
466         if (code)
467             exit(code);
468     }
469
470     if (!uss_DryRun) {
471 #ifdef USS_KAUTH_DB_INSTANCE
472         printf("%s: KAM_DeleteUser: user='%s', CreatorInstance='%s'\n",
473                uss_whoami, a_user, CreatorInstance);
474 #endif /* USS_KAUTH_DB_INSTANCE */
475         if (uss_verbose)
476             printf("Deleting user '%s' from Authentication DB\n",
477                    a_user);
478         code = ubik_Call(KAM_DeleteUser,        /*Procedure to call*/
479                          uconn_kauthP,          /*Ubik client connection struct*/
480                          0,                     /*Flags*/
481                          a_user,                /*User name to delete*/
482                          UserInstance);         /*set in CheckUserName()*/
483         if (code) {
484             if (code == KANOENT) {
485                 if (uss_verbose)
486                     printf("%s: No entry for user '%s' in Authentication DB\n",
487                            uss_whoami, a_user);
488                 return(0);
489             }
490             else {
491                 com_err(uss_whoami, code,
492                         "while deleting entry in Authentication DB\n");
493 #ifdef USS_KAUTH_DB
494                 printf("%s: Error code from KAM_DeleteUser: %d\n",
495                        rn, code);
496 #endif /* USS_KAUTH_DB */
497                 return(code);
498             }
499         } /*KAM_DeleteUser failed*/
500     } /*Not a dry run*/
501     else
502         printf("\t[Dry run - user '%s' NOT deleted from Authentication DB]\n",
503                a_user);
504
505     return(0);
506
507 } /*uss_kauth_DelUser*/
508
509
510 /*-----------------------------------------------------------------------
511  * EXPORTED uss_kauth_CheckUserName
512  *
513  * Environment:
514  *      The user name has already been parsed and placed into
515  *      uss_User.
516  *
517  * Side Effects:
518  *      As advertised.
519  *------------------------------------------------------------------------*/
520
521 afs_int32 uss_kauth_CheckUserName()
522
523 { /*uss_kauth_CheckUserName*/
524
525     static char rn[] = "uss_kauth_CheckUserName";  /*Routine name*/
526     register afs_int32 code;                               /*Return code*/
527
528     if (uss_SkipKaserver) {
529         /*
530          * Don't talk to the kaserver; assume calls succeded and simply return.
531          * Amasingly people want to update it (most likely kerberos) themselves...
532          */
533         if (uss_verbose)
534             printf("[Skip Kaserver option - Checking of user name in Authentication DB not done]\n");
535         return 0;
536     }
537
538     /*
539      * Make sure the module has been initialized before we start trying
540      * to talk to AuthServers.
541      */
542     if (!initDone) {
543         code = InitThisModule();
544         if (code)
545             exit(code);
546     }
547
548     /*
549      * Use the AuthServer's own routine to decide if the parsed user name
550      * is legal.  Specifically, it can't have any weird characters or
551      * embedded instance or cell names. 
552      */
553     code = ka_ParseLoginName(uss_User,
554                              UserPrincipal, UserInstance, UserCell);
555     if (strlen(UserInstance) > 0) {
556         fprintf(stderr, "%s: User name can't have an instance string ('%s')\n",
557                 uss_whoami, UserInstance);
558         return(-1);
559     }
560     if (strlen(UserCell) > 0) {
561         fprintf(stderr, "%s: User name can't have a cell string ('%s')\n",
562                 uss_whoami, UserCell);
563         return(-1);
564     }
565     if (strchr(UserPrincipal, ':') != NULL) {
566         fprintf(stderr, "%s: User name '%s' can't have a colon\n",
567                 uss_whoami, UserPrincipal);
568         return(-1);
569     }
570     if (strlen(UserPrincipal) > 8) {
571         fprintf(stderr,
572                 "%s: User name '%s' must have 8 or fewer characters\n",
573                 uss_whoami, UserPrincipal);
574         return(-1);
575     }
576
577     /*
578      * The name's OK in my book.  Replace the user name with the parsed
579      * value.
580      */
581     strcpy(uss_User, UserPrincipal);
582     return(0);
583
584 } /*uss_kauth_CheckUserName*/
585
586
587 /*
588  * EXPORTED uss_kauth_SetFields
589  *
590  * Environment:
591  *      The uconn_kauthP variable may already be set to an AuthServer
592  *      connection.
593  *
594  * Side Effects:
595  *      As advertised.
596  */
597
598 afs_int32 uss_kauth_SetFields(username, expirestring, reuse, failures, lockout)
599     char *reuse;
600     char *username;
601     char *expirestring;
602     char *failures;
603     char *lockout;
604
605     static char rn[] = "uss_kauth_SetFields";
606     afs_int32 code;
607     char misc_auth_bytes[4];
608     int i;
609     afs_int32 flags = 0;
610     Date expiration = 0;
611     afs_int32 lifetime = 0;
612     afs_int32 maxAssociates = -1;
613     afs_int32 was_spare = 0;
614     char instance = '\0';
615     int pwexpiry;
616     int nfailures, locktime;
617
618     if (strlen (username) > uss_UserLen) {
619         fprintf(stderr,
620                 "%s: * User field in add cmd too long (max is %d chars; truncated value is '%s')\n",
621                 uss_whoami, uss_UserLen, uss_User);
622         return(-1);
623     }
624
625     strcpy (uss_User, username);
626     code = uss_kauth_CheckUserName();
627     if (code)
628       return (code);
629
630     /*  no point in doing this any sooner than necessary */
631     for (i=0;i<4;misc_auth_bytes[i++] = 0);
632
633     pwexpiry = atoi(expirestring);
634     if (pwexpiry <0 || pwexpiry >254) {
635       fprintf(stderr,"Password lifetime range must be [0..254] days.\n");
636       fprintf(stderr,"Zero represents an unlimited lifetime.\n");
637       fprintf(stderr,"Continuing with default lifetime == 0 for user %s.\n",
638               username);
639     }
640     else {
641       misc_auth_bytes[0] = pwexpiry+1;
642     }
643
644     if (!strcmp(reuse, "reuse")) {
645        misc_auth_bytes[1] = KA_REUSEPW;
646      }
647     else if (!strcmp(reuse, "noreuse")) {
648       misc_auth_bytes[1] = KA_NOREUSEPW;
649       }
650     else {
651       misc_auth_bytes[1] = KA_REUSEPW;
652       fprintf(stderr,
653               "must specify \"reuse\" or \"noreuse\": \"reuse\" assumed\n");
654     }
655
656     nfailures = atoi(failures);
657     if (nfailures <0 || nfailures >254) {
658       fprintf(stderr,"Failure limit must be in [0..254].\n");
659       fprintf(stderr,"Zero represents unlimited login attempts.\n");
660       fprintf(stderr,"Continuing with limit == 254 for user %s.\n",
661               username);
662       misc_auth_bytes[2] = 255;
663     }
664     else 
665       misc_auth_bytes[2] = nfailures+1;
666
667     locktime = ktime_Str2int32(lockout);
668     if (locktime < 0 || locktime > 36*60 ) {
669       fprintf(stderr,"Lockout times must be either minutes or hh:mm.\n");
670       fprintf(stderr,"Lockout times must be less than 36 hours.\n");
671       fprintf(stderr,"Continuing with lock time == forever for user %s.\n",
672               username);
673     }
674     else { 
675       locktime = (locktime * 60) >> 9;
676       misc_auth_bytes[3] = locktime +1;
677     }
678       
679     if (uss_SkipKaserver) {
680       if (uss_verbose)
681         printf("[Skipping Kaserver as requested]\n");
682         return 0;
683     }
684
685     /*
686      * Make sure the module has been initialized before we start trying
687      * to talk to AuthServers.
688      */
689     if (!initDone) {
690         code = InitThisModule();
691         if (code)
692             exit(code);
693     }
694
695     if (!uss_DryRun) {
696         if (uss_verbose)
697             fprintf(stderr, "Setting options for '%s' in database.\n",
698                     username);
699         
700         was_spare = pack_long(misc_auth_bytes);
701
702         if (was_spare || flags || expiration || 
703             lifetime || (maxAssociates >= 0)) {
704
705             if (!expiration)
706                 expiration = uss_Expires;
707           code = ubik_Call( KAM_SetFields, uconn_kauthP, 0, username, &instance,
708                             flags, expiration, lifetime, maxAssociates,
709                             was_spare, /* spare */ 0);
710         }
711         else fprintf (stderr,
712                "Must specify one of the optional parameters. Continuing...\n");
713
714         if (code) {
715           com_err (uss_whoami, code,
716                    "calling KAM_SetFields for %s.%s", username, instance);
717
718           return(code);
719         }
720     } /*Not a dry run*/
721     else
722         fprintf(stderr,
723                 "\t[Dry run - user '%s' NOT changed.]\n",
724                 username);
725
726     return(0);
727
728 } /*uss_kauth_SetFields */
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743