Imported Upstream version 1.20.1
[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 int
84 LeashKRB5_renew(void)
85 {
86     krb5_error_code                     code = 0;
87     krb5_context                        ctx = 0;
88     krb5_ccache                         cc = 0;
89     krb5_principal                      me = 0;
90     krb5_principal              server = 0;
91     krb5_creds                          my_creds;
92     krb5_data                   *realm = 0;
93
94     if ( !pkrb5_init_context )
95         goto cleanup;
96
97         memset(&my_creds, 0, sizeof(krb5_creds));
98
99     code = pkrb5_init_context(&ctx);
100     if (code) goto cleanup;
101
102     code = pkrb5_cc_default(ctx, &cc);
103     if (code) goto cleanup;
104
105     code = pkrb5_cc_get_principal(ctx, cc, &me);
106     if (code) goto cleanup;
107
108     realm = krb5_princ_realm(ctx, me);
109
110     code = pkrb5_build_principal_ext(ctx, &server,
111                                     realm->length,realm->data,
112                                     KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
113                                     realm->length,realm->data,
114                                     0);
115     if ( code ) goto cleanup;
116
117     my_creds.client = me;
118     my_creds.server = server;
119
120     pkrb5_cc_set_flags(ctx, cc, 0);
121     code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL);
122     pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET);
123     if (code) {
124         if (code != KRB5KDC_ERR_ETYPE_NOSUPP && code != KRB5_KDC_UNREACH &&
125             code != KRB5_CC_NOTFOUND)
126             Leash_krb5_error(code, "krb5_get_renewed_creds()", 0, &ctx, &cc);
127         goto cleanup;
128     }
129
130     code = pkrb5_cc_initialize(ctx, cc, me);
131     if (code) goto cleanup;
132
133     code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
134     if (code) goto cleanup;
135
136   cleanup:
137     if (my_creds.client == me)
138         my_creds.client = 0;
139     if (my_creds.server == server)
140         my_creds.server = 0;
141     pkrb5_free_cred_contents(ctx, &my_creds);
142     if (me)
143         pkrb5_free_principal(ctx, me);
144     if (server)
145         pkrb5_free_principal(ctx, server);
146     if (cc)
147         pkrb5_cc_close(ctx, cc);
148     if (ctx)
149         pkrb5_free_context(ctx);
150     return(code);
151 }
152
153 static krb5_error_code KRB5_CALLCONV
154 leash_krb5_prompter( krb5_context context,
155                                          void *data,
156                                          const char *name,
157                                          const char *banner,
158                                          int num_prompts,
159                                          krb5_prompt prompts[]);
160
161 int
162 Leash_krb5_kinit(
163 krb5_context alt_ctx,
164 HWND hParent,
165 char *principal_name,
166 char *password,
167 krb5_deltat lifetime,
168 DWORD                       forwardable,
169 DWORD                       proxiable,
170 krb5_deltat                 renew_life,
171 DWORD                       addressless,
172 DWORD                       publicIP
173 )
174 {
175     krb5_error_code                     code = 0;
176     krb5_context                        ctx = 0;
177     krb5_ccache                         cc = 0, defcache = 0;
178     krb5_principal                      me = 0;
179     char*                       name = 0;
180     krb5_creds                          my_creds;
181     krb5_get_init_creds_opt *   options = NULL;
182     krb5_address **             addrs = NULL;
183     int                         i = 0, addr_count = 0;
184     int                         cc_new = 0;
185     const char *                deftype = NULL;
186
187     if (!pkrb5_init_context)
188         return 0;
189
190     memset(&my_creds, 0, sizeof(my_creds));
191
192     if (alt_ctx)
193     {
194         ctx = alt_ctx;
195     }
196     else
197     {
198         code = pkrb5_init_context(&ctx);
199         if (code) goto cleanup;
200     }
201
202     code = pkrb5_get_init_creds_opt_alloc(ctx, &options);
203     if (code) goto cleanup;
204
205     code = pkrb5_cc_default(ctx, &defcache);
206     if (code) goto cleanup;
207
208     code = pkrb5_parse_name(ctx, principal_name, &me);
209     if (code) goto cleanup;
210
211     deftype = pkrb5_cc_get_type(ctx, defcache);
212     if (me != NULL && pkrb5_cc_support_switch(ctx, deftype)) {
213         /* Use an existing cache for the specified principal if we can. */
214         code = pkrb5_cc_cache_match(ctx, me, &cc);
215         if (code != 0 && code != KRB5_CC_NOTFOUND)
216             goto cleanup;
217         if (code == KRB5_CC_NOTFOUND) {
218             code = pkrb5_cc_new_unique(ctx, deftype, NULL, &cc);
219             if (code)
220                 goto cleanup;
221             cc_new = 1;
222         }
223         pkrb5_cc_close(ctx, defcache);
224     } else {
225         cc = defcache;
226     }
227
228     code = pkrb5_unparse_name(ctx, me, &name);
229     if (code) goto cleanup;
230
231     if (lifetime == 0)
232         lifetime = Leash_get_default_lifetime();
233     else
234         lifetime *= 5*60;
235
236         if (renew_life > 0)
237                 renew_life *= 5*60;
238
239     if (lifetime)
240         pkrb5_get_init_creds_opt_set_tkt_life(options, lifetime);
241         pkrb5_get_init_creds_opt_set_forwardable(options,
242                                              forwardable ? 1 : 0);
243         pkrb5_get_init_creds_opt_set_proxiable(options,
244                                            proxiable ? 1 : 0);
245         pkrb5_get_init_creds_opt_set_renew_life(options,
246                                             renew_life);
247     if (addressless)
248         pkrb5_get_init_creds_opt_set_address_list(options,NULL);
249     else {
250                 if (publicIP)
251         {
252             // we are going to add the public IP address specified by the user
253             // to the list provided by the operating system
254             krb5_address ** local_addrs=NULL;
255             DWORD           netIPAddr;
256
257             pkrb5_os_localaddr(ctx, &local_addrs);
258             while ( local_addrs[i++] );
259             addr_count = i + 1;
260
261             addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *));
262             if ( !addrs ) {
263                 pkrb5_free_addresses(ctx, local_addrs);
264                 assert(0);
265             }
266             memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));
267             i = 0;
268             while ( local_addrs[i] ) {
269                 addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
270                 if (addrs[i] == NULL) {
271                     pkrb5_free_addresses(ctx, local_addrs);
272                     assert(0);
273                 }
274
275                 addrs[i]->magic = local_addrs[i]->magic;
276                 addrs[i]->addrtype = local_addrs[i]->addrtype;
277                 addrs[i]->length = local_addrs[i]->length;
278                 addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
279                 if (!addrs[i]->contents) {
280                     pkrb5_free_addresses(ctx, local_addrs);
281                     assert(0);
282                 }
283
284                 memcpy(addrs[i]->contents,local_addrs[i]->contents,
285                         local_addrs[i]->length);        /* safe */
286                 i++;
287             }
288             pkrb5_free_addresses(ctx, local_addrs);
289
290             addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
291             if (addrs[i] == NULL)
292                 assert(0);
293
294             addrs[i]->magic = KV5M_ADDRESS;
295             addrs[i]->addrtype = AF_INET;
296             addrs[i]->length = 4;
297             addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length);
298             if (!addrs[i]->contents)
299                 assert(0);
300
301             netIPAddr = htonl(publicIP);
302             memcpy(addrs[i]->contents,&netIPAddr,4);
303
304             pkrb5_get_init_creds_opt_set_address_list(options,addrs);
305
306         }
307     }
308
309     code = pkrb5_get_init_creds_opt_set_out_ccache(ctx, options, cc);
310     if (code)
311         goto cleanup;
312
313     code = pkrb5_get_init_creds_password(ctx,
314                                        &my_creds,
315                                        me,
316                                        password, // password
317                                        leash_krb5_prompter, // prompter
318                                        hParent, // prompter data
319                                        0, // start time
320                                        0, // service name
321                                        options);
322     // @TODO: make this an option
323     if ((!code) && (cc != defcache)) {
324         code = pkrb5_cc_switch(ctx, cc);
325         if (!code) {
326             const char *cctype = pkrb5_cc_get_type(ctx, cc);
327             if (cctype != NULL) {
328                 char defname[20];
329                 sprintf_s(defname, sizeof(defname), "%s:", cctype);
330                 pkrb5int_cc_user_set_default_name(ctx, defname);
331             }
332         }
333     }
334  cleanup:
335     if (code && cc_new) {
336         // don't leave newly-generated empty ccache lying around on failure
337         pkrb5_cc_destroy(ctx, cc);
338         cc = NULL;
339     }
340     if ( addrs ) {
341         for ( i=0;i<addr_count;i++ ) {
342             if ( addrs[i] ) {
343                 if ( addrs[i]->contents )
344                     free(addrs[i]->contents);
345                 free(addrs[i]);
346             }
347         }
348     }
349     if (my_creds.client == me)
350         my_creds.client = 0;
351     pkrb5_free_cred_contents(ctx, &my_creds);
352     if (name)
353         pkrb5_free_unparsed_name(ctx, name);
354     if (me)
355         pkrb5_free_principal(ctx, me);
356     if (cc)
357         pkrb5_cc_close(ctx, cc);
358     if (options)
359         pkrb5_get_init_creds_opt_free(ctx, options);
360     if (ctx && (ctx != alt_ctx))
361         pkrb5_free_context(ctx);
362     return(code);
363 }
364
365
366 /**************************************/
367 /* LeashKRB5destroyTicket():          */
368 /**************************************/
369 int
370 Leash_krb5_kdestroy(
371     void
372     )
373 {
374     krb5_context                ctx;
375     krb5_ccache                 cache;
376     krb5_error_code             rc;
377
378     ctx = NULL;
379     cache = NULL;
380     rc = Leash_krb5_initialize(&ctx);
381     if (rc)
382         return(rc);
383
384     if (rc = pkrb5_cc_default(ctx, &cache))
385         return(rc);
386
387     rc = pkrb5_cc_destroy(ctx, cache);
388
389     if (ctx != NULL)
390         pkrb5_free_context(ctx);
391
392     return(rc);
393
394 }
395
396 krb5_error_code
397 Leash_krb5_cc_default(krb5_context *ctx, krb5_ccache *cache)
398 {
399     krb5_error_code rc;
400     krb5_flags flags;
401
402     char *functionName = NULL;
403     if (*cache == 0) {
404         rc = pkrb5_cc_default(*ctx, cache);
405         if (rc) {
406             functionName = "krb5_cc_default()";
407             goto on_error;
408         }
409     }
410     flags = KRB5_TC_NOTICKET;
411     rc = pkrb5_cc_set_flags(*ctx, *cache, flags);
412     if (rc) {
413         if (rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) {
414             if (*cache != NULL && *ctx != NULL)
415                 pkrb5_cc_close(*ctx, *cache);
416         } else {
417             functionName = "krb5_cc_set_flags()";
418             goto on_error;
419         }
420     }
421 on_error:
422     if (rc && functionName) {
423         Leash_krb5_error(rc, functionName, 0, ctx, cache);
424     }
425     return rc;
426 }
427
428 /**************************************/
429 /* Leash_krb5_initialize():             */
430 /**************************************/
431 int Leash_krb5_initialize(krb5_context *ctx)
432 {
433     LPCSTR          functionName = NULL;
434     krb5_error_code     rc;
435
436     if (pkrb5_init_context == NULL)
437         return 1;
438
439     if (*ctx == 0) {
440         if (rc = (*pkrb5_init_context)(ctx)) {
441             functionName = "krb5_init_context()";
442             return Leash_krb5_error(rc, functionName, 0, ctx, NULL);
443         }
444     }
445     return 0;
446 }
447
448
449 /**************************************/
450 /* Leash_krb5_error():           */
451 /**************************************/
452 int
453 Leash_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName,
454                  int FreeContextFlag, krb5_context * ctx,
455                  krb5_ccache * cache)
456 {
457 #ifdef USE_MESSAGE_BOX
458     char message[256];
459     const char *errText;
460
461     errText = perror_message(rc);
462     _snprintf(message, sizeof(message),
463               "%s\n(Kerberos error %ld)\n\n%s failed",
464               errText,
465               rc,
466               FailedFunctionName);
467     message[sizeof(message)-1] = 0;
468
469     MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR |
470                MB_TASKMODAL |
471                MB_SETFOREGROUND);
472 #endif /* USE_MESSAGE_BOX */
473
474     if (ctx != NULL && *ctx != NULL) {
475         if (cache != NULL && *cache != NULL) {
476             pkrb5_cc_close(*ctx, *cache);
477             *cache = NULL;
478         }
479
480         if (FreeContextFlag) {
481             pkrb5_free_context(*ctx);
482             *ctx = NULL;
483         }
484     }
485
486     return rc;
487 }
488
489
490 /* User Query data structures and functions */
491
492 struct textField {
493     char * buf;                       /* Destination buffer address */
494     int    len;                       /* Destination buffer length */
495     char * label;                     /* Label for this field */
496     char * def;                       /* Default response for this field */
497     int    echo;                      /* 0 = no, 1 = yes, 2 = asterisks */
498 };
499
500 static int                mid_cnt = 0;
501 static struct textField * mid_tb = NULL;
502
503 #define ID_TEXT       150
504 #define ID_MID_TEXT 300
505
506 static BOOL CALLBACK
507 MultiInputDialogProc( HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam)
508 {
509     int i;
510
511     switch ( message ) {
512     case WM_INITDIALOG:
513         if ( GetDlgCtrlID((HWND) wParam) != ID_MID_TEXT )
514         {
515             SetFocus(GetDlgItem( hDialog, ID_MID_TEXT));
516             return FALSE;
517         }
518                 for ( i=0; i < mid_cnt ; i++ ) {
519                         if (mid_tb[i].echo == 0)
520                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, 32, 0);
521                     else if (mid_tb[i].echo == 2)
522                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, '*', 0);
523                 }
524         return TRUE;
525
526     case WM_COMMAND:
527         switch ( LOWORD(wParam) ) {
528         case IDOK:
529             for ( i=0; i < mid_cnt ; i++ ) {
530                 if ( !GetDlgItemText(hDialog, ID_MID_TEXT+i, mid_tb[i].buf, mid_tb[i].len) )
531                     *mid_tb[i].buf = '\0';
532             }
533             /* fallthrough */
534         case IDCANCEL:
535             EndDialog(hDialog, LOWORD(wParam));
536             return TRUE;
537         }
538     }
539     return FALSE;
540 }
541
542 static LPWORD
543 lpwAlign( LPWORD lpIn )
544 {
545     ULONG ul;
546
547     ul = (ULONG) lpIn;
548     ul += 3;
549     ul >>=2;
550     ul <<=2;
551     return (LPWORD) ul;;
552 }
553
554 /*
555  * dialog widths are measured in 1/4 character widths
556  * dialog height are measured in 1/8 character heights
557  */
558
559 static LRESULT
560 MultiInputDialog( HINSTANCE hinst, HWND hwndOwner,
561                   char * ptext[], int numlines, int width,
562                   int tb_cnt, struct textField * tb)
563 {
564     HGLOBAL hgbl;
565     LPDLGTEMPLATE lpdt;
566     LPDLGITEMTEMPLATE lpdit;
567     LPWORD lpw;
568     LPWSTR lpwsz;
569     LRESULT ret;
570     int nchar, i;
571     size_t pwid;
572
573     hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
574     if (!hgbl)
575         return -1;
576
577     mid_cnt = tb_cnt;
578     mid_tb = tb;
579
580     lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
581
582     // Define a dialog box.
583
584     lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
585                    | DS_MODALFRAME | WS_CAPTION | DS_CENTER
586                    | DS_SETFOREGROUND | DS_3DLOOK
587                    | DS_SHELLFONT | DS_NOFAILCREATE;
588     lpdt->cdit = numlines + (2 * tb_cnt) + 2;  // number of controls
589     lpdt->x  = 10;
590     lpdt->y  = 10;
591     lpdt->cx = 20 + width * 4;
592     lpdt->cy = 20 + (numlines + tb_cnt + 4) * 14;
593
594     lpw = (LPWORD) (lpdt + 1);
595     *lpw++ = 0;   // no menu
596     *lpw++ = 0;   // predefined dialog box class (by default)
597
598     lpwsz = (LPWSTR) lpw;
599     nchar = MultiByteToWideChar (CP_ACP, 0, "", -1, lpwsz, 128);
600     lpw   += nchar;
601     *lpw++ = 8;                        // font size (points)
602     lpwsz = (LPWSTR) lpw;
603     nchar = MultiByteToWideChar (CP_ACP, 0, "MS Shell Dlg",
604                                     -1, lpwsz, 128);
605     lpw   += nchar;
606
607     //-----------------------
608     // Define an OK button.
609     //-----------------------
610     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
611     lpdit = (LPDLGITEMTEMPLATE) lpw;
612     lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP | WS_BORDER;
613     lpdit->dwExtendedStyle = 0;
614     lpdit->x  = (lpdt->cx - 14)/4 - 20;
615     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
616     lpdit->cx = 40;
617     lpdit->cy = 14;
618     lpdit->id = IDOK;  // OK button identifier
619
620     lpw = (LPWORD) (lpdit + 1);
621     *lpw++ = 0xFFFF;
622     *lpw++ = 0x0080;    // button class
623
624     lpwsz = (LPWSTR) lpw;
625     nchar = MultiByteToWideChar (CP_ACP, 0, "OK", -1, lpwsz, 50);
626     lpw   += nchar;
627     *lpw++ = 0;           // no creation data
628
629     //-----------------------
630     // Define an Cancel button.
631     //-----------------------
632     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
633     lpdit = (LPDLGITEMTEMPLATE) lpw;
634     lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP | WS_BORDER;
635     lpdit->dwExtendedStyle = 0;
636     lpdit->x  = (lpdt->cx - 14)*3/4 - 20;
637     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
638     lpdit->cx = 40;
639     lpdit->cy = 14;
640     lpdit->id = IDCANCEL;  // CANCEL button identifier
641
642     lpw = (LPWORD) (lpdit + 1);
643     *lpw++ = 0xFFFF;
644     *lpw++ = 0x0080;    // button class
645
646     lpwsz = (LPWSTR) lpw;
647     nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
648     lpw   += nchar;
649     *lpw++ = 0;           // no creation data
650
651     /* Add controls for preface data */
652     for ( i=0; i<numlines; i++) {
653         /*-----------------------
654          * Define a static text control.
655          *-----------------------*/
656         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
657         lpdit = (LPDLGITEMTEMPLATE) lpw;
658         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
659         lpdit->dwExtendedStyle = 0;
660         lpdit->x  = 10;
661         lpdit->y  = 10 + i * 14;
662         lpdit->cx = strlen(ptext[i]) * 4 + 10;
663         lpdit->cy = 14;
664         lpdit->id = ID_TEXT + i;  // text identifier
665
666         lpw = (LPWORD) (lpdit + 1);
667         *lpw++ = 0xFFFF;
668         *lpw++ = 0x0082;                         // static class
669
670         lpwsz = (LPWSTR) lpw;
671         nchar = MultiByteToWideChar (CP_ACP, 0, ptext[i],
672                                          -1, lpwsz, 2*width);
673         lpw   += nchar;
674         *lpw++ = 0;           // no creation data
675     }
676
677     for ( i=0, pwid = 0; i<tb_cnt; i++) {
678         if ( pwid < strlen(tb[i].label) )
679             pwid = strlen(tb[i].label);
680     }
681
682     for ( i=0; i<tb_cnt; i++) {
683         /* Prompt */
684         /*-----------------------
685          * Define a static text control.
686          *-----------------------*/
687         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
688         lpdit = (LPDLGITEMTEMPLATE) lpw;
689         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
690         lpdit->dwExtendedStyle = 0;
691         lpdit->x  = 10;
692         lpdit->y  = 10 + (numlines + i + 1) * 14;
693         lpdit->cx = pwid * 4;
694         lpdit->cy = 14;
695         lpdit->id = ID_TEXT + numlines + i;  // text identifier
696
697         lpw = (LPWORD) (lpdit + 1);
698         *lpw++ = 0xFFFF;
699         *lpw++ = 0x0082;                         // static class
700
701         lpwsz = (LPWSTR) lpw;
702         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].label ? tb[i].label : "",
703                                      -1, lpwsz, 128);
704         lpw   += nchar;
705         *lpw++ = 0;           // no creation data
706
707         /*-----------------------
708          * Define an edit control.
709          *-----------------------*/
710         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
711         lpdit = (LPDLGITEMTEMPLATE) lpw;
712         lpdit->style = WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | WS_BORDER | (tb[i].echo == 1 ? 0L : ES_PASSWORD);
713         lpdit->dwExtendedStyle = 0;
714         lpdit->x  = 10 + (pwid + 1) * 4;
715         lpdit->y  = 10 + (numlines + i + 1) * 14;
716         lpdit->cx = (width - (pwid + 1)) * 4;
717         lpdit->cy = 14;
718         lpdit->id = ID_MID_TEXT + i;             // identifier
719
720         lpw = (LPWORD) (lpdit + 1);
721         *lpw++ = 0xFFFF;
722         *lpw++ = 0x0081;                         // edit class
723
724         lpwsz = (LPWSTR) lpw;
725         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].def ? tb[i].def : "",
726                                      -1, lpwsz, 128);
727         lpw   += nchar;
728         *lpw++ = 0;           // no creation data
729     }
730
731     GlobalUnlock(hgbl);
732     ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,
733                                                         hwndOwner, (DLGPROC) MultiInputDialogProc);
734     GlobalFree(hgbl);
735
736     switch ( ret ) {
737     case 0:     /* Timeout */
738         return -1;
739     case IDOK:
740         return 1;
741     case IDCANCEL:
742         return 0;
743     default: {
744         char buf[256];
745         sprintf(buf,"DialogBoxIndirect() failed: %d",GetLastError());
746         MessageBox(hwndOwner,
747                     buf,
748                     "GetLastError()",
749                     MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
750         return -1;
751     }
752     }
753 }
754
755 static int
756 multi_field_dialog(HWND hParent, char * preface, int n, struct textField tb[])
757 {
758         extern HINSTANCE hLeashInst;
759     size_t maxwidth = 0;
760     int numlines = 0;
761     size_t len;
762     char * plines[16], *p = preface ? preface : "";
763     int i;
764
765     for ( i=0; i<16; i++ )
766         plines[i] = NULL;
767
768     while (*p && numlines < 16) {
769         plines[numlines++] = p;
770         for ( ;*p && *p != '\r' && *p != '\n'; p++ );
771         if ( *p == '\r' && *(p+1) == '\n' ) {
772             *p++ = '\0';
773             p++;
774         } else if ( *p == '\n' ) {
775             *p++ = '\0';
776         }
777         if ( strlen(plines[numlines-1]) > maxwidth )
778             maxwidth = strlen(plines[numlines-1]);
779     }
780
781     for ( i=0;i<n;i++ ) {
782         len = strlen(tb[i].label) + 1 + (tb[i].len > 40 ? 40 : tb[i].len);
783         if ( maxwidth < len )
784             maxwidth = len;
785     }
786
787     return(MultiInputDialog(hLeashInst, hParent, plines, numlines, maxwidth, n, tb));
788 }
789
790 static krb5_error_code KRB5_CALLCONV
791 leash_krb5_prompter( krb5_context context,
792                                          void *data,
793                                          const char *name,
794                                          const char *banner,
795                                          int num_prompts,
796                                          krb5_prompt prompts[])
797 {
798     krb5_error_code     errcode = 0;
799     int                 i;
800     struct textField * tb = NULL;
801     int    len = 0, blen=0, nlen=0;
802         HWND hParent = (HWND)data;
803
804     if (name)
805         nlen = strlen(name)+2;
806
807     if (banner)
808         blen = strlen(banner)+2;
809
810     tb = (struct textField *) malloc(sizeof(struct textField) * num_prompts);
811     if ( tb != NULL ) {
812         int ok;
813         memset(tb,0,sizeof(struct textField) * num_prompts);
814         for ( i=0; i < num_prompts; i++ ) {
815             tb[i].buf = prompts[i].reply->data;
816             tb[i].len = prompts[i].reply->length;
817             tb[i].label = prompts[i].prompt;
818             tb[i].def = NULL;
819             tb[i].echo = (prompts[i].hidden ? 2 : 1);
820         }
821
822         ok = multi_field_dialog(hParent,(char *)banner,num_prompts,tb);
823         if ( ok ) {
824             for ( i=0; i < num_prompts; i++ )
825                 prompts[i].reply->length = strlen(prompts[i].reply->data);
826         } else
827             errcode = -2;
828     }
829
830     if ( tb )
831         free(tb);
832     if (errcode) {
833         for (i = 0; i < num_prompts; i++) {
834             memset(prompts[i].reply->data, 0, prompts[i].reply->length);
835         }
836     }
837     return errcode;
838 }