3911720aeaeeeb682581ef76eacc83819fddc6fa
[platform/upstream/krb5.git] / src / windows / leashdll / krb5routines.c
1 // Module name: krb5routines.c
2
3 #include <windows.h>
4 #define SECURITY_WIN32
5 #include <security.h>
6
7 /* _WIN32_WINNT must be 0x0501 or greater to pull in definition of
8  * all required LSA data types when the Vista SDK NtSecAPI.h is used.
9  */
10 #ifndef _WIN32_WINNT
11 #define _WIN32_WINNT 0x0501
12 #else
13 #if _WIN32_WINNT < 0x0501
14 #undef _WIN32_WINNT
15 #define _WIN32_WINNT 0x0501
16 #endif
17 #endif
18 #include <ntsecapi.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <time.h>
22 #include <assert.h>
23
24 #include <winsock2.h>
25
26 /* Private Include files */
27 #include "leashdll.h"
28 #include <leashwin.h>
29 #include "leash-int.h"
30
31 #define KRB5_DEFAULT_LIFE            60*60*10 /* 10 hours */
32
33 char *GetTicketFlag(krb5_creds *cred)
34 {
35    static char buf[32];
36    int i = 0;
37
38    buf[i++] = ' ';
39    buf[i++] = '(';
40
41    if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
42      buf[i++] = 'F';
43
44    if (cred->ticket_flags & TKT_FLG_FORWARDED)
45      buf[i++] = 'f';
46
47    if (cred->ticket_flags & TKT_FLG_PROXIABLE)
48      buf[i++] = 'P';
49
50    if (cred->ticket_flags & TKT_FLG_PROXY)
51      buf[i++] = 'p';
52
53    if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
54      buf[i++] = 'D';
55
56    if (cred->ticket_flags & TKT_FLG_POSTDATED)
57      buf[i++] = 'd';
58
59    if (cred->ticket_flags & TKT_FLG_INVALID)
60      buf[i++] = 'i';
61
62    if (cred->ticket_flags & TKT_FLG_RENEWABLE)
63      buf[i++] = 'R';
64
65    if (cred->ticket_flags & TKT_FLG_INITIAL)
66      buf[i++] = 'I';
67
68    if (cred->ticket_flags & TKT_FLG_HW_AUTH)
69      buf[i++] = 'H';
70
71    if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
72      buf[i++] = 'A';
73
74    buf[i++] = ')';
75    buf[i] = '\0';
76
77    if (i <= 3)
78      buf[0] = '\0';
79
80    return buf;
81 }
82
83 long
84 Leash_convert524(
85      krb5_context alt_ctx
86      )
87 {
88 #if defined(NO_KRB5) || defined(NO_KRB4)
89     return(0);
90 #else
91     krb5_context ctx = 0;
92     krb5_error_code code = 0;
93     int icode = 0;
94     krb5_principal me = 0;
95     krb5_principal server = 0;
96     krb5_creds *v5creds = 0;
97     krb5_creds increds;
98     krb5_ccache cc = 0;
99     CREDENTIALS * v4creds = NULL;
100     static int init_ets = 1;
101
102     if (!pkrb5_init_context ||
103         !pkrb_in_tkt ||
104         !pkrb524_init_ets ||
105         !pkrb524_convert_creds_kdc)
106         return 0;
107
108         v4creds = (CREDENTIALS *) malloc(sizeof(CREDENTIALS));
109         memset((char *) v4creds, 0, sizeof(CREDENTIALS));
110
111     memset((char *) &increds, 0, sizeof(increds));
112     /*
113       From this point on, we can goto cleanup because increds is
114       initialized.
115     */
116
117     if (alt_ctx)
118     {
119         ctx = alt_ctx;
120     }
121     else
122     {
123         code = pkrb5_init_context(&ctx);
124         if (code) goto cleanup;
125     }
126
127     code = pkrb5_cc_default(ctx, &cc);
128     if (code) goto cleanup;
129
130     if ( init_ets ) {
131         pkrb524_init_ets(ctx);
132         init_ets = 0;
133     }
134
135     if (code = pkrb5_cc_get_principal(ctx, cc, &me))
136         goto cleanup;
137
138     if ((code = pkrb5_build_principal(ctx,
139                                      &server,
140                                      krb5_princ_realm(ctx, me)->length,
141                                      krb5_princ_realm(ctx, me)->data,
142                                      "krbtgt",
143                                      krb5_princ_realm(ctx, me)->data,
144                                      NULL))) {
145         goto cleanup;
146     }
147
148     increds.client = me;
149     increds.server = server;
150     increds.times.endtime = 0;
151     increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC;
152     if ((code = pkrb5_get_credentials(ctx, 0,
153                                      cc,
154                                      &increds,
155                                      &v5creds))) {
156         goto cleanup;
157     }
158
159     if ((icode = pkrb524_convert_creds_kdc(ctx,
160                                           v5creds,
161                                           v4creds))) {
162         goto cleanup;
163     }
164
165     /* initialize ticket cache */
166     if ((icode = pkrb_in_tkt(v4creds->pname, v4creds->pinst, v4creds->realm)
167          != KSUCCESS)) {
168         goto cleanup;
169     }
170     /* stash ticket, session key, etc. for future use */
171     if ((icode = pkrb_save_credentials(v4creds->service,
172                                       v4creds->instance,
173                                       v4creds->realm,
174                                       v4creds->session,
175                                       v4creds->lifetime,
176                                       v4creds->kvno,
177                                       &(v4creds->ticket_st),
178                                       v4creds->issue_date))) {
179         goto cleanup;
180     }
181
182  cleanup:
183     memset(v4creds, 0, sizeof(v4creds));
184     free(v4creds);
185
186     if (v5creds) {
187         pkrb5_free_creds(ctx, v5creds);
188     }
189     if (increds.client == me)
190         me = 0;
191     if (increds.server == server)
192         server = 0;
193     pkrb5_free_cred_contents(ctx, &increds);
194     if (server) {
195         pkrb5_free_principal(ctx, server);
196     }
197     if (me) {
198         pkrb5_free_principal(ctx, me);
199     }
200     pkrb5_cc_close(ctx, cc);
201
202     if (ctx && (ctx != alt_ctx)) {
203         pkrb5_free_context(ctx);
204     }
205     return !(code || icode);
206 #endif /* NO_KRB5 */
207 }
208
209
210 int
211 LeashKRB5_renew(void)
212 {
213 #ifdef NO_KRB5
214     return(0);
215 #else
216     krb5_error_code                     code = 0;
217     krb5_context                        ctx = 0;
218     krb5_ccache                         cc = 0;
219     krb5_principal                      me = 0;
220     krb5_principal              server = 0;
221     krb5_creds                          my_creds;
222     krb5_data                   *realm = 0;
223
224     if ( !pkrb5_init_context )
225         goto cleanup;
226
227         memset(&my_creds, 0, sizeof(krb5_creds));
228
229     code = pkrb5_init_context(&ctx);
230     if (code) goto cleanup;
231
232     code = pkrb5_cc_default(ctx, &cc);
233     if (code) goto cleanup;
234
235     code = pkrb5_cc_get_principal(ctx, cc, &me);
236     if (code) goto cleanup;
237
238     realm = krb5_princ_realm(ctx, me);
239
240     code = pkrb5_build_principal_ext(ctx, &server,
241                                     realm->length,realm->data,
242                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
243                                     realm->length,realm->data,
244                                     0);
245     if ( code ) goto cleanup;
246
247     my_creds.client = me;
248     my_creds.server = server;
249
250 #ifdef KRB5_TC_NOTICKET
251     pkrb5_cc_set_flags(ctx, cc, 0);
252 #endif
253     code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL);
254 #ifdef KRB5_TC_NOTICKET
255     pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET);
256 #endif
257     if (code) {
258         if ( code != KRB5KDC_ERR_ETYPE_NOSUPP ||
259              code != KRB5_KDC_UNREACH)
260             Leash_krb5_error(code, "krb5_get_renewed_creds()", 0, &ctx, &cc);
261         goto cleanup;
262     }
263
264     code = pkrb5_cc_initialize(ctx, cc, me);
265     if (code) goto cleanup;
266
267     code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
268     if (code) goto cleanup;
269
270   cleanup:
271     if (my_creds.client == me)
272         my_creds.client = 0;
273     if (my_creds.server == server)
274         my_creds.server = 0;
275     pkrb5_free_cred_contents(ctx, &my_creds);
276     if (me)
277         pkrb5_free_principal(ctx, me);
278     if (server)
279         pkrb5_free_principal(ctx, server);
280     if (cc)
281         pkrb5_cc_close(ctx, cc);
282     if (ctx)
283         pkrb5_free_context(ctx);
284     return(code);
285 #endif /* NO_KRB5 */
286 }
287
288 #ifndef NO_KRB5
289 static krb5_error_code KRB5_CALLCONV
290 leash_krb5_prompter( krb5_context context,
291                                          void *data,
292                                          const char *name,
293                                          const char *banner,
294                                          int num_prompts,
295                                          krb5_prompt prompts[]);
296 #endif /* NO_KRB5 */
297
298 int
299 Leash_krb5_kinit(
300 krb5_context alt_ctx,
301 HWND hParent,
302 char *principal_name,
303 char *password,
304 krb5_deltat lifetime,
305 DWORD                       forwardable,
306 DWORD                       proxiable,
307 krb5_deltat                 renew_life,
308 DWORD                       addressless,
309 DWORD                       publicIP
310 )
311 {
312 #ifdef NO_KRB5
313     return(0);
314 #else
315     krb5_error_code                     code = 0;
316     krb5_context                        ctx = 0;
317     krb5_ccache                         cc = 0, defcache = 0;
318     krb5_principal                      me = 0;
319     char*                       name = 0;
320     krb5_creds                          my_creds;
321     krb5_get_init_creds_opt *   options = NULL;
322     krb5_address **             addrs = NULL;
323     int                         i = 0, addr_count = 0;
324     int                         cc_new = 0;
325     const char *                deftype = NULL;
326
327     if (!pkrb5_init_context)
328         return 0;
329
330     memset(&my_creds, 0, sizeof(my_creds));
331
332     if (alt_ctx)
333     {
334         ctx = alt_ctx;
335     }
336     else
337     {
338         code = pkrb5_init_context(&ctx);
339         if (code) goto cleanup;
340     }
341
342     code = pkrb5_get_init_creds_opt_alloc(ctx, &options);
343     if (code) goto cleanup;
344
345     code = pkrb5_cc_default(ctx, &defcache);
346     if (code) goto cleanup;
347
348     code = pkrb5_parse_name(ctx, principal_name, &me);
349     if (code) goto cleanup;
350
351     deftype = pkrb5_cc_get_type(ctx, defcache);
352     if (me != NULL && pkrb5_cc_support_switch(ctx, deftype)) {
353         /* Use an existing cache for the specified principal if we can. */
354         code = pkrb5_cc_cache_match(ctx, me, &cc);
355         if (code != 0 && code != KRB5_CC_NOTFOUND)
356             goto cleanup;
357         if (code == KRB5_CC_NOTFOUND) {
358             code = pkrb5_cc_new_unique(ctx, deftype, NULL, &cc);
359             if (code)
360                 goto cleanup;
361             cc_new = 1;
362         }
363         pkrb5_cc_close(ctx, defcache);
364     } else {
365         cc = defcache;
366     }
367
368     code = pkrb5_unparse_name(ctx, me, &name);
369     if (code) goto cleanup;
370
371     if (lifetime == 0)
372         lifetime = Leash_get_default_lifetime();
373     else
374         lifetime *= 5*60;
375
376         if (renew_life > 0)
377                 renew_life *= 5*60;
378
379     if (lifetime)
380         pkrb5_get_init_creds_opt_set_tkt_life(options, lifetime);
381         pkrb5_get_init_creds_opt_set_forwardable(options,
382                                              forwardable ? 1 : 0);
383         pkrb5_get_init_creds_opt_set_proxiable(options,
384                                            proxiable ? 1 : 0);
385         pkrb5_get_init_creds_opt_set_renew_life(options,
386                                             renew_life);
387     if (addressless)
388         pkrb5_get_init_creds_opt_set_address_list(options,NULL);
389     else {
390                 if (publicIP)
391         {
392             // we are going to add the public IP address specified by the user
393             // to the list provided by the operating system
394             krb5_address ** local_addrs=NULL;
395             DWORD           netIPAddr;
396
397             pkrb5_os_localaddr(ctx, &local_addrs);
398             while ( local_addrs[i++] );
399             addr_count = i + 1;
400
401             addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *));
402             if ( !addrs ) {
403                 pkrb5_free_addresses(ctx, local_addrs);
404                 assert(0);
405             }
406             memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));
407             i = 0;
408             while ( local_addrs[i] ) {
409                 addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
410                 if (addrs[i] == NULL) {
411                     pkrb5_free_addresses(ctx, local_addrs);
412                     assert(0);
413                 }
414
415                 addrs[i]->magic = local_addrs[i]->magic;
416                 addrs[i]->addrtype = local_addrs[i]->addrtype;
417                 addrs[i]->length = local_addrs[i]->length;
418                 addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
419                 if (!addrs[i]->contents) {
420                     pkrb5_free_addresses(ctx, local_addrs);
421                     assert(0);
422                 }
423
424                 memcpy(addrs[i]->contents,local_addrs[i]->contents,
425                         local_addrs[i]->length);        /* safe */
426                 i++;
427             }
428             pkrb5_free_addresses(ctx, local_addrs);
429
430             addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
431             if (addrs[i] == NULL)
432                 assert(0);
433
434             addrs[i]->magic = KV5M_ADDRESS;
435             addrs[i]->addrtype = AF_INET;
436             addrs[i]->length = 4;
437             addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
438             if (!addrs[i]->contents)
439                 assert(0);
440
441             netIPAddr = htonl(publicIP);
442             memcpy(addrs[i]->contents,&netIPAddr,4);
443
444             pkrb5_get_init_creds_opt_set_address_list(options,addrs);
445
446         }
447     }
448
449     code = pkrb5_get_init_creds_opt_set_out_ccache(ctx, options, cc);
450     if (code)
451         goto cleanup;
452
453     code = pkrb5_get_init_creds_password(ctx,
454                                        &my_creds,
455                                        me,
456                                        password, // password
457                                        leash_krb5_prompter, // prompter
458                                        hParent, // prompter data
459                                        0, // start time
460                                        0, // service name
461                                        options);
462     // @TODO: make this an option
463     if ((!code) && (cc != defcache)) {
464         code = pkrb5_cc_switch(ctx, cc);
465         if (!code) {
466             const char *cctype = pkrb5_cc_get_type(ctx, cc);
467             if (cctype != NULL) {
468                 char defname[20];
469                 sprintf_s(defname, sizeof(defname), "%s:", cctype);
470                 pkrb5int_cc_user_set_default_name(ctx, defname);
471             }
472         }
473     }
474  cleanup:
475     if (code && cc_new) {
476         // don't leave newly-generated empty ccache lying around on failure
477         pkrb5_cc_destroy(ctx, cc);
478         cc = NULL;
479     }
480     if ( addrs ) {
481         for ( i=0;i<addr_count;i++ ) {
482             if ( addrs[i] ) {
483                 if ( addrs[i]->contents )
484                     free(addrs[i]->contents);
485                 free(addrs[i]);
486             }
487         }
488     }
489     if (my_creds.client == me)
490         my_creds.client = 0;
491     pkrb5_free_cred_contents(ctx, &my_creds);
492     if (name)
493         pkrb5_free_unparsed_name(ctx, name);
494     if (me)
495         pkrb5_free_principal(ctx, me);
496     if (cc)
497         pkrb5_cc_close(ctx, cc);
498     if (options)
499         pkrb5_get_init_creds_opt_free(ctx, options);
500     if (ctx && (ctx != alt_ctx))
501         pkrb5_free_context(ctx);
502     return(code);
503 #endif //!NO_KRB5
504 }
505
506
507 /**************************************/
508 /* LeashKRB5destroyTicket():          */
509 /**************************************/
510 int
511 Leash_krb5_kdestroy(
512     void
513     )
514 {
515 #ifdef NO_KRB5
516     return(0);
517 #else
518     krb5_context                ctx;
519     krb5_ccache                 cache;
520     krb5_error_code             rc;
521
522     ctx = NULL;
523     cache = NULL;
524     rc = Leash_krb5_initialize(&ctx);
525     if (rc)
526         return(rc);
527
528     if (rc = pkrb5_cc_default(ctx, &cache))
529         return(rc);
530
531     rc = pkrb5_cc_destroy(ctx, cache);
532
533     if (ctx != NULL)
534         pkrb5_free_context(ctx);
535
536     return(rc);
537
538 #endif //!NO_KRB5
539 }
540
541 krb5_error_code
542 Leash_krb5_cc_default(krb5_context *ctx, krb5_ccache *cache)
543 {
544     krb5_error_code rc;
545     krb5_flags flags;
546
547     char *functionName = NULL;
548     if (*cache == 0) {
549         rc = pkrb5_cc_default(*ctx, cache);
550         if (rc) {
551             functionName = "krb5_cc_default()";
552             goto on_error;
553         }
554     }
555 #ifdef KRB5_TC_NOTICKET
556     flags = KRB5_TC_NOTICKET;
557 #endif
558     rc = pkrb5_cc_set_flags(*ctx, *cache, flags);
559     if (rc) {
560         if (rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) {
561             if (*cache != NULL && *ctx != NULL)
562                 pkrb5_cc_close(*ctx, *cache);
563         } else {
564             functionName = "krb5_cc_set_flags()";
565             goto on_error;
566         }
567     }
568 on_error:
569     if (rc && functionName) {
570         Leash_krb5_error(rc, functionName, 0, ctx, cache);
571     }
572     return rc;
573 }
574
575 /**************************************/
576 /* Leash_krb5_initialize():             */
577 /**************************************/
578 int Leash_krb5_initialize(krb5_context *ctx)
579 {
580 #ifdef NO_KRB5
581     return(0);
582 #else
583
584     LPCSTR          functionName = NULL;
585     krb5_error_code     rc;
586
587     if (pkrb5_init_context == NULL)
588         return 1;
589
590     if (*ctx == 0) {
591         if (rc = (*pkrb5_init_context)(ctx)) {
592             functionName = "krb5_init_context()";
593             return Leash_krb5_error(rc, functionName, 0, ctx, NULL);
594         }
595     }
596     return 0;
597 #endif //!NO_KRB5
598 }
599
600
601 /**************************************/
602 /* Leash_krb5_error():           */
603 /**************************************/
604 int
605 Leash_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName,
606                  int FreeContextFlag, krb5_context * ctx,
607                  krb5_ccache * cache)
608 {
609 #ifdef NO_KRB5
610     return 0;
611 #else
612 #ifdef USE_MESSAGE_BOX
613     char message[256];
614     const char *errText;
615
616     errText = perror_message(rc);
617     _snprintf(message, sizeof(message),
618               "%s\n(Kerberos error %ld)\n\n%s failed",
619               errText,
620               rc,
621               FailedFunctionName);
622     message[sizeof(message)-1] = 0;
623
624     MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR |
625                MB_TASKMODAL |
626                MB_SETFOREGROUND);
627 #endif /* USE_MESSAGE_BOX */
628
629     if (ctx != NULL && *ctx != NULL) {
630         if (cache != NULL && *cache != NULL) {
631             pkrb5_cc_close(*ctx, *cache);
632             *cache = NULL;
633         }
634
635         if (FreeContextFlag) {
636             pkrb5_free_context(*ctx);
637             *ctx = NULL;
638         }
639     }
640
641     return rc;
642
643 #endif //!NO_KRB5
644 }
645
646
647 BOOL
648 Leash_ms2mit(BOOL save_creds)
649 {
650 #ifdef NO_KRB5
651     return(FALSE);
652 #else /* NO_KRB5 */
653     krb5_context kcontext = 0;
654     krb5_error_code code;
655     krb5_ccache ccache=0;
656     krb5_ccache mslsa_ccache=0;
657     krb5_creds creds;
658     krb5_cc_cursor cursor=0;
659     krb5_principal princ = 0;
660     BOOL rc = FALSE;
661
662     if ( !pkrb5_init_context )
663         goto cleanup;
664
665     if (code = pkrb5_init_context(&kcontext))
666         goto cleanup;
667
668     if (code = pkrb5_cc_resolve(kcontext, "MSLSA:", &mslsa_ccache))
669         goto cleanup;
670
671     if ( save_creds ) {
672         if (code = pkrb5_cc_get_principal(kcontext, mslsa_ccache, &princ))
673             goto cleanup;
674
675         if (code = pkrb5_cc_default(kcontext, &ccache))
676             goto cleanup;
677
678         if (code = pkrb5_cc_initialize(kcontext, ccache, princ))
679             goto cleanup;
680
681         if (code = pkrb5_cc_copy_creds(kcontext, mslsa_ccache, ccache))
682             goto cleanup;
683
684         rc = TRUE;
685     } else {
686         /* Enumerate tickets from cache looking for an initial ticket */
687         if ((code = pkrb5_cc_start_seq_get(kcontext, mslsa_ccache, &cursor)))
688             goto cleanup;
689
690         while (!(code = pkrb5_cc_next_cred(kcontext, mslsa_ccache, &cursor, &creds)))
691         {
692             if ( creds.ticket_flags & TKT_FLG_INITIAL ) {
693                 rc = TRUE;
694                 pkrb5_free_cred_contents(kcontext, &creds);
695                 break;
696             }
697             pkrb5_free_cred_contents(kcontext, &creds);
698         }
699         pkrb5_cc_end_seq_get(kcontext, mslsa_ccache, &cursor);
700     }
701
702   cleanup:
703     if (princ)
704         pkrb5_free_principal(kcontext, princ);
705     if (ccache)
706         pkrb5_cc_close(kcontext, ccache);
707     if (mslsa_ccache)
708         pkrb5_cc_close(kcontext, mslsa_ccache);
709     if (kcontext)
710         pkrb5_free_context(kcontext);
711     return(rc);
712 #endif /* NO_KRB5 */
713 }
714
715
716 #ifndef NO_KRB5
717 /* User Query data structures and functions */
718
719 struct textField {
720     char * buf;                       /* Destination buffer address */
721     int    len;                       /* Destination buffer length */
722     char * label;                     /* Label for this field */
723     char * def;                       /* Default response for this field */
724     int    echo;                      /* 0 = no, 1 = yes, 2 = asterisks */
725 };
726
727 static int                mid_cnt = 0;
728 static struct textField * mid_tb = NULL;
729
730 #define ID_TEXT       150
731 #define ID_MID_TEXT 300
732
733 static BOOL CALLBACK
734 MultiInputDialogProc( HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam)
735 {
736     int i;
737
738     switch ( message ) {
739     case WM_INITDIALOG:
740         if ( GetDlgCtrlID((HWND) wParam) != ID_MID_TEXT )
741         {
742             SetFocus(GetDlgItem( hDialog, ID_MID_TEXT));
743             return FALSE;
744         }
745                 for ( i=0; i < mid_cnt ; i++ ) {
746                         if (mid_tb[i].echo == 0)
747                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, 32, 0);
748                     else if (mid_tb[i].echo == 2)
749                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, '*', 0);
750                 }
751         return TRUE;
752
753     case WM_COMMAND:
754         switch ( LOWORD(wParam) ) {
755         case IDOK:
756             for ( i=0; i < mid_cnt ; i++ ) {
757                 if ( !GetDlgItemText(hDialog, ID_MID_TEXT+i, mid_tb[i].buf, mid_tb[i].len) )
758                     *mid_tb[i].buf = '\0';
759             }
760             /* fallthrough */
761         case IDCANCEL:
762             EndDialog(hDialog, LOWORD(wParam));
763             return TRUE;
764         }
765     }
766     return FALSE;
767 }
768
769 static LPWORD
770 lpwAlign( LPWORD lpIn )
771 {
772     ULONG ul;
773
774     ul = (ULONG) lpIn;
775     ul += 3;
776     ul >>=2;
777     ul <<=2;
778     return (LPWORD) ul;;
779 }
780
781 /*
782  * dialog widths are measured in 1/4 character widths
783  * dialog height are measured in 1/8 character heights
784  */
785
786 static LRESULT
787 MultiInputDialog( HINSTANCE hinst, HWND hwndOwner,
788                   char * ptext[], int numlines, int width,
789                   int tb_cnt, struct textField * tb)
790 {
791     HGLOBAL hgbl;
792     LPDLGTEMPLATE lpdt;
793     LPDLGITEMTEMPLATE lpdit;
794     LPWORD lpw;
795     LPWSTR lpwsz;
796     LRESULT ret;
797     int nchar, i;
798     size_t pwid;
799
800     hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
801     if (!hgbl)
802         return -1;
803
804     mid_cnt = tb_cnt;
805     mid_tb = tb;
806
807     lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
808
809     // Define a dialog box.
810
811     lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
812                    | DS_MODALFRAME | WS_CAPTION | DS_CENTER
813                    | DS_SETFOREGROUND | DS_3DLOOK
814                    | DS_SHELLFONT | DS_NOFAILCREATE;
815     lpdt->cdit = numlines + (2 * tb_cnt) + 2;  // number of controls
816     lpdt->x  = 10;
817     lpdt->y  = 10;
818     lpdt->cx = 20 + width * 4;
819     lpdt->cy = 20 + (numlines + tb_cnt + 4) * 14;
820
821     lpw = (LPWORD) (lpdt + 1);
822     *lpw++ = 0;   // no menu
823     *lpw++ = 0;   // predefined dialog box class (by default)
824
825     lpwsz = (LPWSTR) lpw;
826     nchar = MultiByteToWideChar (CP_ACP, 0, "", -1, lpwsz, 128);
827     lpw   += nchar;
828     *lpw++ = 8;                        // font size (points)
829     lpwsz = (LPWSTR) lpw;
830     nchar = MultiByteToWideChar (CP_ACP, 0, "MS Shell Dlg",
831                                     -1, lpwsz, 128);
832     lpw   += nchar;
833
834     //-----------------------
835     // Define an OK button.
836     //-----------------------
837     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
838     lpdit = (LPDLGITEMTEMPLATE) lpw;
839     lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP | WS_BORDER;
840     lpdit->dwExtendedStyle = 0;
841     lpdit->x  = (lpdt->cx - 14)/4 - 20;
842     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
843     lpdit->cx = 40;
844     lpdit->cy = 14;
845     lpdit->id = IDOK;  // OK button identifier
846
847     lpw = (LPWORD) (lpdit + 1);
848     *lpw++ = 0xFFFF;
849     *lpw++ = 0x0080;    // button class
850
851     lpwsz = (LPWSTR) lpw;
852     nchar = MultiByteToWideChar (CP_ACP, 0, "OK", -1, lpwsz, 50);
853     lpw   += nchar;
854     *lpw++ = 0;           // no creation data
855
856     //-----------------------
857     // Define an Cancel button.
858     //-----------------------
859     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
860     lpdit = (LPDLGITEMTEMPLATE) lpw;
861     lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP | WS_BORDER;
862     lpdit->dwExtendedStyle = 0;
863     lpdit->x  = (lpdt->cx - 14)*3/4 - 20;
864     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
865     lpdit->cx = 40;
866     lpdit->cy = 14;
867     lpdit->id = IDCANCEL;  // CANCEL button identifier
868
869     lpw = (LPWORD) (lpdit + 1);
870     *lpw++ = 0xFFFF;
871     *lpw++ = 0x0080;    // button class
872
873     lpwsz = (LPWSTR) lpw;
874     nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
875     lpw   += nchar;
876     *lpw++ = 0;           // no creation data
877
878     /* Add controls for preface data */
879     for ( i=0; i<numlines; i++) {
880         /*-----------------------
881          * Define a static text control.
882          *-----------------------*/
883         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
884         lpdit = (LPDLGITEMTEMPLATE) lpw;
885         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
886         lpdit->dwExtendedStyle = 0;
887         lpdit->x  = 10;
888         lpdit->y  = 10 + i * 14;
889         lpdit->cx = strlen(ptext[i]) * 4 + 10;
890         lpdit->cy = 14;
891         lpdit->id = ID_TEXT + i;  // text identifier
892
893         lpw = (LPWORD) (lpdit + 1);
894         *lpw++ = 0xFFFF;
895         *lpw++ = 0x0082;                         // static class
896
897         lpwsz = (LPWSTR) lpw;
898         nchar = MultiByteToWideChar (CP_ACP, 0, ptext[i],
899                                          -1, lpwsz, 2*width);
900         lpw   += nchar;
901         *lpw++ = 0;           // no creation data
902     }
903
904     for ( i=0, pwid = 0; i<tb_cnt; i++) {
905         if ( pwid < strlen(tb[i].label) )
906             pwid = strlen(tb[i].label);
907     }
908
909     for ( i=0; i<tb_cnt; i++) {
910         /* Prompt */
911         /*-----------------------
912          * Define a static text control.
913          *-----------------------*/
914         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
915         lpdit = (LPDLGITEMTEMPLATE) lpw;
916         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
917         lpdit->dwExtendedStyle = 0;
918         lpdit->x  = 10;
919         lpdit->y  = 10 + (numlines + i + 1) * 14;
920         lpdit->cx = pwid * 4;
921         lpdit->cy = 14;
922         lpdit->id = ID_TEXT + numlines + i;  // text identifier
923
924         lpw = (LPWORD) (lpdit + 1);
925         *lpw++ = 0xFFFF;
926         *lpw++ = 0x0082;                         // static class
927
928         lpwsz = (LPWSTR) lpw;
929         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].label ? tb[i].label : "",
930                                      -1, lpwsz, 128);
931         lpw   += nchar;
932         *lpw++ = 0;           // no creation data
933
934         /*-----------------------
935          * Define an edit control.
936          *-----------------------*/
937         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
938         lpdit = (LPDLGITEMTEMPLATE) lpw;
939         lpdit->style = WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | WS_BORDER | (tb[i].echo == 1 ? 0L : ES_PASSWORD);
940         lpdit->dwExtendedStyle = 0;
941         lpdit->x  = 10 + (pwid + 1) * 4;
942         lpdit->y  = 10 + (numlines + i + 1) * 14;
943         lpdit->cx = (width - (pwid + 1)) * 4;
944         lpdit->cy = 14;
945         lpdit->id = ID_MID_TEXT + i;             // identifier
946
947         lpw = (LPWORD) (lpdit + 1);
948         *lpw++ = 0xFFFF;
949         *lpw++ = 0x0081;                         // edit class
950
951         lpwsz = (LPWSTR) lpw;
952         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].def ? tb[i].def : "",
953                                      -1, lpwsz, 128);
954         lpw   += nchar;
955         *lpw++ = 0;           // no creation data
956     }
957
958     GlobalUnlock(hgbl);
959     ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,
960                                                         hwndOwner, (DLGPROC) MultiInputDialogProc);
961     GlobalFree(hgbl);
962
963     switch ( ret ) {
964     case 0:     /* Timeout */
965         return -1;
966     case IDOK:
967         return 1;
968     case IDCANCEL:
969         return 0;
970     default: {
971         char buf[256];
972         sprintf(buf,"DialogBoxIndirect() failed: %d",GetLastError());
973         MessageBox(hwndOwner,
974                     buf,
975                     "GetLastError()",
976                     MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
977         return -1;
978     }
979     }
980 }
981
982 static int
983 multi_field_dialog(HWND hParent, char * preface, int n, struct textField tb[])
984 {
985         extern HINSTANCE hLeashInst;
986     size_t maxwidth = 0;
987     int numlines = 0;
988     size_t len;
989     char * plines[16], *p = preface ? preface : "";
990     int i;
991
992     for ( i=0; i<16; i++ )
993         plines[i] = NULL;
994
995     while (*p && numlines < 16) {
996         plines[numlines++] = p;
997         for ( ;*p && *p != '\r' && *p != '\n'; p++ );
998         if ( *p == '\r' && *(p+1) == '\n' ) {
999             *p++ = '\0';
1000             p++;
1001         } else if ( *p == '\n' ) {
1002             *p++ = '\0';
1003         }
1004         if ( strlen(plines[numlines-1]) > maxwidth )
1005             maxwidth = strlen(plines[numlines-1]);
1006     }
1007
1008     for ( i=0;i<n;i++ ) {
1009         len = strlen(tb[i].label) + 1 + (tb[i].len > 40 ? 40 : tb[i].len);
1010         if ( maxwidth < len )
1011             maxwidth = len;
1012     }
1013
1014     return(MultiInputDialog(hLeashInst, hParent, plines, numlines, maxwidth, n, tb));
1015 }
1016
1017 static krb5_error_code KRB5_CALLCONV
1018 leash_krb5_prompter( krb5_context context,
1019                                          void *data,
1020                                          const char *name,
1021                                          const char *banner,
1022                                          int num_prompts,
1023                                          krb5_prompt prompts[])
1024 {
1025     krb5_error_code     errcode = 0;
1026     int                 i;
1027     struct textField * tb = NULL;
1028     int    len = 0, blen=0, nlen=0;
1029         HWND hParent = (HWND)data;
1030
1031     if (name)
1032         nlen = strlen(name)+2;
1033
1034     if (banner)
1035         blen = strlen(banner)+2;
1036
1037     tb = (struct textField *) malloc(sizeof(struct textField) * num_prompts);
1038     if ( tb != NULL ) {
1039         int ok;
1040         memset(tb,0,sizeof(struct textField) * num_prompts);
1041         for ( i=0; i < num_prompts; i++ ) {
1042             tb[i].buf = prompts[i].reply->data;
1043             tb[i].len = prompts[i].reply->length;
1044             tb[i].label = prompts[i].prompt;
1045             tb[i].def = NULL;
1046             tb[i].echo = (prompts[i].hidden ? 2 : 1);
1047         }
1048
1049         ok = multi_field_dialog(hParent,(char *)banner,num_prompts,tb);
1050         if ( ok ) {
1051             for ( i=0; i < num_prompts; i++ )
1052                 prompts[i].reply->length = strlen(prompts[i].reply->data);
1053         } else
1054             errcode = -2;
1055     }
1056
1057     if ( tb )
1058         free(tb);
1059     if (errcode) {
1060         for (i = 0; i < num_prompts; i++) {
1061             memset(prompts[i].reply->data, 0, prompts[i].reply->length);
1062         }
1063     }
1064     return errcode;
1065 }
1066 #endif /* NO_KRB5 */