Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / lib / auth / sia / sia.c
1 /*
2  * Copyright (c) 1995-1999 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "sia_locl.h"
35
36 RCSID("$Id: sia.c,v 1.32.2.1 1999/12/20 09:49:30 joda Exp $");
37
38 int 
39 siad_init(void)
40 {
41     return SIADSUCCESS;
42 }
43
44 int 
45 siad_chk_invoker(void)
46 {
47     SIA_DEBUG(("DEBUG", "siad_chk_invoker"));
48     return SIADFAIL;
49 }
50
51 int 
52 siad_ses_init(SIAENTITY *entity, int pkgind)
53 {
54     struct state *s = malloc(sizeof(*s));
55     SIA_DEBUG(("DEBUG", "siad_ses_init"));
56     if(s == NULL)
57         return SIADFAIL;
58     memset(s, 0, sizeof(*s));
59 #ifdef SIA_KRB5
60     krb5_init_context(&s->context);
61 #endif
62     entity->mech[pkgind] = (int*)s;
63     return SIADSUCCESS;
64 }
65
66 static int
67 setup_name(SIAENTITY *e, prompt_t *p)
68 {
69     SIA_DEBUG(("DEBUG", "setup_name"));
70     e->name = malloc(SIANAMEMIN + 1);
71     if(e->name == NULL){
72         SIA_DEBUG(("DEBUG", "failed to malloc %u bytes", SIANAMEMIN+1));
73         return SIADFAIL;
74     }
75     p->prompt = (unsigned char*)"login: ";
76     p->result = (unsigned char*)e->name;
77     p->min_result_length = 1;
78     p->max_result_length = SIANAMEMIN;
79     p->control_flags = 0;
80     return SIADSUCCESS;
81 }
82
83 static int
84 setup_password(SIAENTITY *e, prompt_t *p)
85 {
86     SIA_DEBUG(("DEBUG", "setup_password"));
87     e->password = malloc(SIAMXPASSWORD + 1);
88     if(e->password == NULL){
89         SIA_DEBUG(("DEBUG", "failed to malloc %u bytes", SIAMXPASSWORD+1));
90         return SIADFAIL;
91     }
92     p->prompt = (unsigned char*)"Password: ";
93     p->result = (unsigned char*)e->password;
94     p->min_result_length = 0;
95     p->max_result_length = SIAMXPASSWORD;
96     p->control_flags = SIARESINVIS;
97     return SIADSUCCESS;
98 }
99
100
101 static int
102 doauth(SIAENTITY *entity, int pkgind, char *name)
103 {
104     struct passwd pw, *pwd;
105     char pwbuf[1024];
106     struct state *s = (struct state*)entity->mech[pkgind];
107 #ifdef SIA_KRB5
108     krb5_realm *realms, *r;
109     krb5_principal principal;
110     krb5_ccache ccache;
111     krb5_error_code ret;
112 #endif
113 #ifdef SIA_KRB4
114     char realm[REALM_SZ];
115     char *toname, *toinst;
116     int ret;
117     struct passwd fpw, *fpwd;
118     char fpwbuf[1024];
119     int secure;
120 #endif
121         
122     if(getpwnam_r(name, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0){
123         SIA_DEBUG(("DEBUG", "failed to getpwnam(%s)", name));
124         return SIADFAIL;
125     }
126
127 #ifdef SIA_KRB5
128     ret = krb5_get_default_realms(s->context, &realms);
129
130     for (r = realms; *r != NULL; ++r) {
131         krb5_make_principal (s->context, &principal, *r, entity->name, NULL);
132
133         if(krb5_kuserok(s->context, principal, entity->name))
134             break;
135     }
136     krb5_free_host_realm (s->context, realms);
137     if (*r == NULL)
138         return SIADFAIL;
139
140     sprintf(s->ticket, "FILE:/tmp/krb5_cc%d_%d", pwd->pw_uid, getpid());
141     ret = krb5_cc_resolve(s->context, s->ticket, &ccache);
142     if(ret)
143         return SIADFAIL;
144 #endif
145         
146 #ifdef SIA_KRB4
147     snprintf(s->ticket, sizeof(s->ticket),
148              "%s%u_%u", TKT_ROOT, (unsigned)pwd->pw_uid, (unsigned)getpid());
149     krb_get_lrealm(realm, 1);
150     toname = name;
151     toinst = "";
152     if(entity->authtype == SIA_A_SUAUTH){
153         uid_t ouid;
154 #ifdef HAVE_SIAENTITY_OUID
155         ouid = entity->ouid;
156 #else
157         ouid = getuid();
158 #endif
159         if(getpwuid_r(ouid, &fpw, fpwbuf, sizeof(fpwbuf), &fpwd) != 0){
160             SIA_DEBUG(("DEBUG", "failed to getpwuid(%u)", ouid));
161             return SIADFAIL;
162         }
163         snprintf(s->ticket, sizeof(s->ticket), "%s_%s_to_%s_%d", 
164                  TKT_ROOT, fpwd->pw_name, pwd->pw_name, getpid());
165         if(strcmp(pwd->pw_name, "root") == 0){
166             toname = fpwd->pw_name;
167             toinst = pwd->pw_name;
168         }
169     }
170     if(entity->authtype == SIA_A_REAUTH) 
171         snprintf(s->ticket, sizeof(s->ticket), "%s", tkt_string());
172     
173     krb_set_tkt_string(s->ticket);
174         
175     setuid(0); /* XXX fix for fix in tf_util.c */
176     if(krb_kuserok(toname, toinst, realm, name)){
177         SIA_DEBUG(("DEBUG", "%s.%s@%s is not allowed to login as %s", 
178                    toname, toinst, realm, name));
179         return SIADFAIL;
180     }
181 #endif
182 #ifdef SIA_KRB5
183     ret = krb5_verify_user_lrealm(s->context, principal, ccache,
184                                   entity->password, 1, NULL);
185     if(ret){
186         /* if this is most likely a local user (such as
187            root), just silently return failure when the
188            principal doesn't exist */
189         if(ret != KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN && 
190            ret != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
191             SIALOG("WARNING", "krb5_verify_user(%s): %s", 
192                    entity->name, error_message(ret));
193         return SIADFAIL;
194     }
195 #endif
196 #ifdef SIA_KRB4
197     if (getuid () == 0)
198         secure = KRB_VERIFY_SECURE;
199     else
200         secure = KRB_VERIFY_NOT_SECURE;
201         
202     ret = krb_verify_user(toname, toinst, realm,
203                           entity->password, secure, NULL);
204     if(ret){
205         SIA_DEBUG(("DEBUG", "krb_verify_user: %s", krb_get_err_text(ret)));
206         if(ret != KDC_PR_UNKNOWN)
207             /* since this is most likely a local user (such as
208                root), just silently return failure when the
209                principal doesn't exist */
210             SIALOG("WARNING", "krb_verify_user(%s.%s): %s", 
211                    toname, toinst, krb_get_err_text(ret));
212         return SIADFAIL;
213     }
214 #endif
215     if(sia_make_entity_pwd(pwd, entity) == SIAFAIL)
216         return SIADFAIL;
217     s->valid = 1;
218     return SIADSUCCESS;
219 }
220
221
222 static int 
223 common_auth(sia_collect_func_t *collect, 
224             SIAENTITY *entity, 
225             int siastat,
226             int pkgind)
227 {
228     prompt_t prompts[2], *pr;
229     char *name;
230
231     SIA_DEBUG(("DEBUG", "common_auth"));
232     if((siastat == SIADSUCCESS) && (geteuid() == 0))
233         return SIADSUCCESS;
234     if(entity == NULL) {
235         SIA_DEBUG(("DEBUG", "entity == NULL"));
236         return SIADFAIL | SIADSTOP;
237     }
238     name = entity->name;
239     if(entity->acctname)
240         name = entity->acctname;
241     
242     if((collect != NULL) && entity->colinput) {
243         int num;
244         pr = prompts;
245         if(name == NULL){
246             if(setup_name(entity, pr) != SIADSUCCESS)
247                 return SIADFAIL;
248             pr++;
249         }
250         if(entity->password == NULL){
251             if(setup_password(entity, pr) != SIADSUCCESS)
252                 return SIADFAIL;
253             pr++;
254         }
255         num = pr - prompts;
256         if(num == 1){
257             if((*collect)(240, SIAONELINER, (unsigned char*)"", num, 
258                           prompts) != SIACOLSUCCESS){
259                 SIA_DEBUG(("DEBUG", "collect failed"));
260                 return SIADFAIL | SIADSTOP;
261             }
262         } else if(num > 0){
263             if((*collect)(0, SIAFORM, (unsigned char*)"", num, 
264                           prompts) != SIACOLSUCCESS){
265                 SIA_DEBUG(("DEBUG", "collect failed"));
266                 return SIADFAIL | SIADSTOP;
267             }
268         }
269     }
270     if(name == NULL)
271         name = entity->name;
272     if(name == NULL || name[0] == '\0'){
273         SIA_DEBUG(("DEBUG", "name is null"));
274         return SIADFAIL;
275     }
276
277     if(entity->password == NULL || strlen(entity->password) > SIAMXPASSWORD){
278         SIA_DEBUG(("DEBUG", "entity->password is null"));
279         return SIADFAIL;
280     }
281     
282     return doauth(entity, pkgind, name);
283 }
284
285
286 int 
287 siad_ses_authent(sia_collect_func_t *collect, 
288                  SIAENTITY *entity, 
289                  int siastat,
290                  int pkgind)
291 {
292     SIA_DEBUG(("DEBUG", "siad_ses_authent"));
293     return common_auth(collect, entity, siastat, pkgind);
294 }
295
296 int 
297 siad_ses_estab(sia_collect_func_t *collect, 
298                SIAENTITY *entity, int pkgind)
299 {
300     SIA_DEBUG(("DEBUG", "siad_ses_estab"));
301     return SIADFAIL;
302 }
303
304 int 
305 siad_ses_launch(sia_collect_func_t *collect,
306                 SIAENTITY *entity,
307                 int pkgind)
308 {
309     static char env[MaxPathLen];
310     struct state *s = (struct state*)entity->mech[pkgind];
311     SIA_DEBUG(("DEBUG", "siad_ses_launch"));
312     if(s->valid){
313 #ifdef SIA_KRB5
314         chown(s->ticket + sizeof("FILE:") - 1, 
315               entity->pwd->pw_uid, 
316               entity->pwd->pw_gid);
317         snprintf(env, sizeof(env), "KRB5CCNAME=%s", s->ticket);
318 #endif
319 #ifdef SIA_KRB4
320         chown(s->ticket, entity->pwd->pw_uid, entity->pwd->pw_gid);
321         snprintf(env, sizeof(env), "KRBTKFILE=%s", s->ticket);
322 #endif
323         putenv(env);
324     }
325 #ifdef KRB4
326     if (k_hasafs()) {
327         char cell[64];
328         k_setpag();
329         if(k_afs_cell_of_file(entity->pwd->pw_dir, cell, sizeof(cell)) == 0)
330             krb_afslog(cell, 0);
331         krb_afslog_home(0, 0, entity->pwd->pw_dir);
332     }
333 #endif
334     return SIADSUCCESS;
335 }
336
337 int 
338 siad_ses_release(SIAENTITY *entity, int pkgind)
339 {
340     SIA_DEBUG(("DEBUG", "siad_ses_release"));
341     if(entity->mech[pkgind]){
342 #ifdef SIA_KRB5
343         struct state *s = (struct state*)entity->mech[pkgind];
344         krb5_free_context(s->context);
345 #endif
346         free(entity->mech[pkgind]);
347     }
348     return SIADSUCCESS;
349 }
350
351 int 
352 siad_ses_suauthent(sia_collect_func_t *collect,
353                    SIAENTITY *entity,
354                    int siastat,
355                    int pkgind)
356 {
357     SIA_DEBUG(("DEBUG", "siad_ses_suauth"));
358     if(geteuid() != 0)
359         return SIADFAIL;
360     if(entity->name == NULL)
361         return SIADFAIL;
362     if(entity->name[0] == '\0') {
363         free(entity->name);
364         entity->name = strdup("root");
365         if (entity->name == NULL)
366             return SIADFAIL;
367     }
368     return common_auth(collect, entity, siastat, pkgind);
369 }
370
371 int
372 siad_ses_reauthent (sia_collect_func_t *collect,
373                     SIAENTITY *entity,
374                     int siastat,
375                     int pkgind)
376 {
377     int ret;
378     SIA_DEBUG(("DEBUG", "siad_ses_reauthent"));
379     if(entity == NULL || entity->name == NULL)
380         return SIADFAIL;
381     ret = common_auth(collect, entity, siastat, pkgind);
382     if((ret & SIADSUCCESS)){
383         /* launch isn't (always?) called when doing reauth, so we must
384            duplicate some code here... */
385         struct state *s = (struct state*)entity->mech[pkgind];
386         chown(s->ticket, entity->pwd->pw_uid, entity->pwd->pw_gid);
387 #ifdef KRB4
388         if(k_hasafs()) {
389             char cell[64];
390             if(k_afs_cell_of_file(entity->pwd->pw_dir, 
391                                   cell, sizeof(cell)) == 0)
392                 krb_afslog(cell, 0);
393             krb_afslog_home(0, 0, entity->pwd->pw_dir);
394         }
395 #endif
396     }
397     return ret;
398 }
399
400 int
401 siad_chg_finger (sia_collect_func_t *collect,
402                      const char *username, 
403                      int argc, 
404                      char *argv[])
405 {
406     SIA_DEBUG(("DEBUG", "siad_chg_finger"));
407     return SIADFAIL;
408 }
409
410 #ifdef SIA_KRB5
411 int
412 siad_chg_password (sia_collect_func_t *collect,
413                      const char *username, 
414                      int argc, 
415                      char *argv[])
416 {
417     return SIADFAIL;
418 }
419 #endif
420
421 #ifdef SIA_KRB4
422 static void
423 sia_message(sia_collect_func_t *collect, int rendition, 
424             const char *title, const char *message)
425 {
426     prompt_t prompt;
427     prompt.prompt = (unsigned char*)message;
428     (*collect)(0, rendition, (unsigned char*)title, 1, &prompt);
429 }
430
431 static int
432 init_change(sia_collect_func_t *collect, krb_principal *princ)
433 {
434     prompt_t prompt;
435     char old_pw[MAX_KPW_LEN+1];
436     char *msg;
437     char tktstring[128];
438     int ret;
439     
440     SIA_DEBUG(("DEBUG", "init_change"));
441     prompt.prompt = (unsigned char*)"Old password: ";
442     prompt.result = (unsigned char*)old_pw;
443     prompt.min_result_length = 0;
444     prompt.max_result_length = sizeof(old_pw) - 1;
445     prompt.control_flags = SIARESINVIS;
446     asprintf(&msg, "Changing password for %s", krb_unparse_name(princ));
447     if(msg == NULL){
448         SIA_DEBUG(("DEBUG", "out of memory"));
449         return SIADFAIL;
450     }
451     ret = (*collect)(60, SIAONELINER, (unsigned char*)msg, 1, &prompt);
452     free(msg);
453     SIA_DEBUG(("DEBUG", "ret = %d", ret));
454     if(ret != SIACOLSUCCESS)
455         return SIADFAIL;
456     snprintf(tktstring, sizeof(tktstring), 
457              "%s_cpw_%u", TKT_ROOT, (unsigned)getpid());
458     krb_set_tkt_string(tktstring);
459     
460     ret = krb_get_pw_in_tkt(princ->name, princ->instance, princ->realm, 
461                             PWSERV_NAME, KADM_SINST, 1, old_pw);
462     if (ret != KSUCCESS) {
463         SIA_DEBUG(("DEBUG", "krb_get_pw_in_tkt: %s", krb_get_err_text(ret)));
464         if (ret == INTK_BADPW)
465             sia_message(collect, SIAWARNING, "", "Incorrect old password.");
466         else
467             sia_message(collect, SIAWARNING, "", "Kerberos error.");
468         memset(old_pw, 0, sizeof(old_pw));
469         return SIADFAIL;
470     }
471     if(chown(tktstring, getuid(), -1) < 0){
472         dest_tkt();
473         return SIADFAIL;
474     }
475     memset(old_pw, 0, sizeof(old_pw));
476     return SIADSUCCESS;
477 }
478
479 int
480 siad_chg_password (sia_collect_func_t *collect,
481                    const char *username, 
482                    int argc, 
483                    char *argv[])
484 {
485     prompt_t prompts[2];
486     krb_principal princ;
487     int ret;
488     char new_pw1[MAX_KPW_LEN+1];
489     char new_pw2[MAX_KPW_LEN+1];
490     static struct et_list *et_list;
491
492     set_progname(argv[0]);
493
494     SIA_DEBUG(("DEBUG", "siad_chg_password"));
495     if(collect == NULL)
496         return SIADFAIL;
497
498     if(username == NULL)
499         username = getlogin();
500
501     ret = krb_parse_name(username, &princ);
502     if(ret)
503         return SIADFAIL;
504     if(princ.realm[0] == '\0')
505         krb_get_lrealm(princ.realm, 1);
506
507     if(et_list == NULL) {
508         initialize_kadm_error_table_r(&et_list);
509         initialize_krb_error_table_r(&et_list);
510     }
511
512     ret = init_change(collect, &princ);
513     if(ret != SIADSUCCESS)
514         return ret;
515
516 again:
517     prompts[0].prompt = (unsigned char*)"New password: ";
518     prompts[0].result = (unsigned char*)new_pw1;
519     prompts[0].min_result_length = MIN_KPW_LEN;
520     prompts[0].max_result_length = sizeof(new_pw1) - 1;
521     prompts[0].control_flags = SIARESINVIS;
522     prompts[1].prompt = (unsigned char*)"Verify new password: ";
523     prompts[1].result = (unsigned char*)new_pw2;
524     prompts[1].min_result_length = MIN_KPW_LEN;
525     prompts[1].max_result_length = sizeof(new_pw2) - 1;
526     prompts[1].control_flags = SIARESINVIS;
527     if((*collect)(120, SIAFORM, (unsigned char*)"", 2, prompts) != 
528        SIACOLSUCCESS) {
529         dest_tkt();
530         return SIADFAIL;
531     }
532     if(strcmp(new_pw1, new_pw2) != 0){
533         sia_message(collect, SIAWARNING, "", "Password mismatch.");
534         goto again;
535     }
536     ret = kadm_check_pw(new_pw1);
537     if(ret) {
538         sia_message(collect, SIAWARNING, "", com_right(et_list, ret));
539         goto again;
540     }
541     
542     memset(new_pw2, 0, sizeof(new_pw2));
543     ret = kadm_init_link (PWSERV_NAME, KRB_MASTER, princ.realm);
544     if (ret != KADM_SUCCESS)
545         sia_message(collect, SIAWARNING, "Error initing kadmin connection", 
546                     com_right(et_list, ret));
547     else {
548         des_cblock newkey;
549         char *pw_msg; /* message from server */
550
551         des_string_to_key(new_pw1, &newkey);
552         ret = kadm_change_pw_plain((unsigned char*)&newkey, new_pw1, &pw_msg);
553         memset(newkey, 0, sizeof(newkey));
554       
555         if (ret == KADM_INSECURE_PW)
556             sia_message(collect, SIAWARNING, "Insecure password", pw_msg);
557         else if (ret != KADM_SUCCESS)
558             sia_message(collect, SIAWARNING, "Error changing password", 
559                         com_right(et_list, ret));
560     }
561     memset(new_pw1, 0, sizeof(new_pw1));
562
563     if (ret != KADM_SUCCESS)
564         sia_message(collect, SIAWARNING, "", "Password NOT changed.");
565     else
566         sia_message(collect, SIAINFO, "", "Password changed.");
567     
568     dest_tkt();
569     if(ret)
570         return SIADFAIL;
571     return SIADSUCCESS;
572 }
573 #endif
574
575 int
576 siad_chg_shell (sia_collect_func_t *collect,
577                      const char *username, 
578                      int argc, 
579                      char *argv[])
580 {
581     return SIADFAIL;
582 }
583
584 int
585 siad_getpwent(struct passwd *result, 
586               char *buf, 
587               int bufsize, 
588               struct sia_context *context)
589 {
590     return SIADFAIL;
591 }
592
593 int
594 siad_getpwuid (uid_t uid, 
595                struct passwd *result, 
596                char *buf, 
597                int bufsize, 
598                struct sia_context *context)
599 {
600     return SIADFAIL;
601 }
602
603 int
604 siad_getpwnam (const char *name, 
605                struct passwd *result, 
606                char *buf, 
607                int bufsize, 
608                struct sia_context *context)
609 {
610     return SIADFAIL;
611 }
612
613 int
614 siad_setpwent (struct sia_context *context)
615 {
616     return SIADFAIL;
617 }
618
619 int
620 siad_endpwent (struct sia_context *context)
621 {
622     return SIADFAIL;
623 }
624
625 int
626 siad_getgrent(struct group *result, 
627               char *buf, 
628               int bufsize, 
629               struct sia_context *context)
630 {
631     return SIADFAIL;
632 }
633
634 int
635 siad_getgrgid (gid_t gid, 
636                struct group *result, 
637                char *buf, 
638                int bufsize, 
639                struct sia_context *context)
640 {
641     return SIADFAIL;
642 }
643
644 int
645 siad_getgrnam (const char *name, 
646                struct group *result, 
647                char *buf, 
648                int bufsize, 
649                struct sia_context *context)
650 {
651     return SIADFAIL;
652 }
653
654 int
655 siad_setgrent (struct sia_context *context)
656 {
657     return SIADFAIL;
658 }
659
660 int
661 siad_endgrent (struct sia_context *context)
662 {
663     return SIADFAIL;
664 }
665
666 int
667 siad_chk_user (const char *logname, int checkflag)
668 {
669     if(checkflag != CHGPASSWD)
670         return SIADFAIL;
671     return SIADSUCCESS;
672 }