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