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