Fix autoconf 2.70 compatibility
[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 ||
125              code != KRB5_KDC_UNREACH)
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 BOOL
491 Leash_ms2mit(BOOL save_creds)
492 {
493     krb5_context kcontext = 0;
494     krb5_error_code code;
495     krb5_ccache ccache=0;
496     krb5_ccache mslsa_ccache=0;
497     krb5_creds creds;
498     krb5_cc_cursor cursor=0;
499     krb5_principal princ = 0;
500     BOOL rc = FALSE;
501
502     if ( !pkrb5_init_context )
503         goto cleanup;
504
505     if (code = pkrb5_init_context(&kcontext))
506         goto cleanup;
507
508     if (code = pkrb5_cc_resolve(kcontext, "MSLSA:", &mslsa_ccache))
509         goto cleanup;
510
511     if ( save_creds ) {
512         if (code = pkrb5_cc_get_principal(kcontext, mslsa_ccache, &princ))
513             goto cleanup;
514
515         if (code = pkrb5_cc_default(kcontext, &ccache))
516             goto cleanup;
517
518         if (code = pkrb5_cc_initialize(kcontext, ccache, princ))
519             goto cleanup;
520
521         if (code = pkrb5_cc_copy_creds(kcontext, mslsa_ccache, ccache))
522             goto cleanup;
523
524         rc = TRUE;
525     } else {
526         /* Enumerate tickets from cache looking for an initial ticket */
527         if ((code = pkrb5_cc_start_seq_get(kcontext, mslsa_ccache, &cursor)))
528             goto cleanup;
529
530         while (!(code = pkrb5_cc_next_cred(kcontext, mslsa_ccache, &cursor, &creds)))
531         {
532             if ( creds.ticket_flags & TKT_FLG_INITIAL ) {
533                 rc = TRUE;
534                 pkrb5_free_cred_contents(kcontext, &creds);
535                 break;
536             }
537             pkrb5_free_cred_contents(kcontext, &creds);
538         }
539         pkrb5_cc_end_seq_get(kcontext, mslsa_ccache, &cursor);
540     }
541
542   cleanup:
543     if (princ)
544         pkrb5_free_principal(kcontext, princ);
545     if (ccache)
546         pkrb5_cc_close(kcontext, ccache);
547     if (mslsa_ccache)
548         pkrb5_cc_close(kcontext, mslsa_ccache);
549     if (kcontext)
550         pkrb5_free_context(kcontext);
551     return(rc);
552 }
553
554
555 /* User Query data structures and functions */
556
557 struct textField {
558     char * buf;                       /* Destination buffer address */
559     int    len;                       /* Destination buffer length */
560     char * label;                     /* Label for this field */
561     char * def;                       /* Default response for this field */
562     int    echo;                      /* 0 = no, 1 = yes, 2 = asterisks */
563 };
564
565 static int                mid_cnt = 0;
566 static struct textField * mid_tb = NULL;
567
568 #define ID_TEXT       150
569 #define ID_MID_TEXT 300
570
571 static BOOL CALLBACK
572 MultiInputDialogProc( HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam)
573 {
574     int i;
575
576     switch ( message ) {
577     case WM_INITDIALOG:
578         if ( GetDlgCtrlID((HWND) wParam) != ID_MID_TEXT )
579         {
580             SetFocus(GetDlgItem( hDialog, ID_MID_TEXT));
581             return FALSE;
582         }
583                 for ( i=0; i < mid_cnt ; i++ ) {
584                         if (mid_tb[i].echo == 0)
585                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, 32, 0);
586                     else if (mid_tb[i].echo == 2)
587                                 SendDlgItemMessage(hDialog, ID_MID_TEXT+i, EM_SETPASSWORDCHAR, '*', 0);
588                 }
589         return TRUE;
590
591     case WM_COMMAND:
592         switch ( LOWORD(wParam) ) {
593         case IDOK:
594             for ( i=0; i < mid_cnt ; i++ ) {
595                 if ( !GetDlgItemText(hDialog, ID_MID_TEXT+i, mid_tb[i].buf, mid_tb[i].len) )
596                     *mid_tb[i].buf = '\0';
597             }
598             /* fallthrough */
599         case IDCANCEL:
600             EndDialog(hDialog, LOWORD(wParam));
601             return TRUE;
602         }
603     }
604     return FALSE;
605 }
606
607 static LPWORD
608 lpwAlign( LPWORD lpIn )
609 {
610     ULONG ul;
611
612     ul = (ULONG) lpIn;
613     ul += 3;
614     ul >>=2;
615     ul <<=2;
616     return (LPWORD) ul;;
617 }
618
619 /*
620  * dialog widths are measured in 1/4 character widths
621  * dialog height are measured in 1/8 character heights
622  */
623
624 static LRESULT
625 MultiInputDialog( HINSTANCE hinst, HWND hwndOwner,
626                   char * ptext[], int numlines, int width,
627                   int tb_cnt, struct textField * tb)
628 {
629     HGLOBAL hgbl;
630     LPDLGTEMPLATE lpdt;
631     LPDLGITEMTEMPLATE lpdit;
632     LPWORD lpw;
633     LPWSTR lpwsz;
634     LRESULT ret;
635     int nchar, i;
636     size_t pwid;
637
638     hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
639     if (!hgbl)
640         return -1;
641
642     mid_cnt = tb_cnt;
643     mid_tb = tb;
644
645     lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
646
647     // Define a dialog box.
648
649     lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU
650                    | DS_MODALFRAME | WS_CAPTION | DS_CENTER
651                    | DS_SETFOREGROUND | DS_3DLOOK
652                    | DS_SHELLFONT | DS_NOFAILCREATE;
653     lpdt->cdit = numlines + (2 * tb_cnt) + 2;  // number of controls
654     lpdt->x  = 10;
655     lpdt->y  = 10;
656     lpdt->cx = 20 + width * 4;
657     lpdt->cy = 20 + (numlines + tb_cnt + 4) * 14;
658
659     lpw = (LPWORD) (lpdt + 1);
660     *lpw++ = 0;   // no menu
661     *lpw++ = 0;   // predefined dialog box class (by default)
662
663     lpwsz = (LPWSTR) lpw;
664     nchar = MultiByteToWideChar (CP_ACP, 0, "", -1, lpwsz, 128);
665     lpw   += nchar;
666     *lpw++ = 8;                        // font size (points)
667     lpwsz = (LPWSTR) lpw;
668     nchar = MultiByteToWideChar (CP_ACP, 0, "MS Shell Dlg",
669                                     -1, lpwsz, 128);
670     lpw   += nchar;
671
672     //-----------------------
673     // Define an OK button.
674     //-----------------------
675     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
676     lpdit = (LPDLGITEMTEMPLATE) lpw;
677     lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP | WS_BORDER;
678     lpdit->dwExtendedStyle = 0;
679     lpdit->x  = (lpdt->cx - 14)/4 - 20;
680     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
681     lpdit->cx = 40;
682     lpdit->cy = 14;
683     lpdit->id = IDOK;  // OK button identifier
684
685     lpw = (LPWORD) (lpdit + 1);
686     *lpw++ = 0xFFFF;
687     *lpw++ = 0x0080;    // button class
688
689     lpwsz = (LPWSTR) lpw;
690     nchar = MultiByteToWideChar (CP_ACP, 0, "OK", -1, lpwsz, 50);
691     lpw   += nchar;
692     *lpw++ = 0;           // no creation data
693
694     //-----------------------
695     // Define an Cancel button.
696     //-----------------------
697     lpw = lpwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
698     lpdit = (LPDLGITEMTEMPLATE) lpw;
699     lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP | WS_BORDER;
700     lpdit->dwExtendedStyle = 0;
701     lpdit->x  = (lpdt->cx - 14)*3/4 - 20;
702     lpdit->y  = 10 + (numlines + tb_cnt + 2) * 14;
703     lpdit->cx = 40;
704     lpdit->cy = 14;
705     lpdit->id = IDCANCEL;  // CANCEL button identifier
706
707     lpw = (LPWORD) (lpdit + 1);
708     *lpw++ = 0xFFFF;
709     *lpw++ = 0x0080;    // button class
710
711     lpwsz = (LPWSTR) lpw;
712     nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
713     lpw   += nchar;
714     *lpw++ = 0;           // no creation data
715
716     /* Add controls for preface data */
717     for ( i=0; i<numlines; i++) {
718         /*-----------------------
719          * Define a static text control.
720          *-----------------------*/
721         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
722         lpdit = (LPDLGITEMTEMPLATE) lpw;
723         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
724         lpdit->dwExtendedStyle = 0;
725         lpdit->x  = 10;
726         lpdit->y  = 10 + i * 14;
727         lpdit->cx = strlen(ptext[i]) * 4 + 10;
728         lpdit->cy = 14;
729         lpdit->id = ID_TEXT + i;  // text identifier
730
731         lpw = (LPWORD) (lpdit + 1);
732         *lpw++ = 0xFFFF;
733         *lpw++ = 0x0082;                         // static class
734
735         lpwsz = (LPWSTR) lpw;
736         nchar = MultiByteToWideChar (CP_ACP, 0, ptext[i],
737                                          -1, lpwsz, 2*width);
738         lpw   += nchar;
739         *lpw++ = 0;           // no creation data
740     }
741
742     for ( i=0, pwid = 0; i<tb_cnt; i++) {
743         if ( pwid < strlen(tb[i].label) )
744             pwid = strlen(tb[i].label);
745     }
746
747     for ( i=0; i<tb_cnt; i++) {
748         /* Prompt */
749         /*-----------------------
750          * Define a static text control.
751          *-----------------------*/
752         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
753         lpdit = (LPDLGITEMTEMPLATE) lpw;
754         lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
755         lpdit->dwExtendedStyle = 0;
756         lpdit->x  = 10;
757         lpdit->y  = 10 + (numlines + i + 1) * 14;
758         lpdit->cx = pwid * 4;
759         lpdit->cy = 14;
760         lpdit->id = ID_TEXT + numlines + i;  // text identifier
761
762         lpw = (LPWORD) (lpdit + 1);
763         *lpw++ = 0xFFFF;
764         *lpw++ = 0x0082;                         // static class
765
766         lpwsz = (LPWSTR) lpw;
767         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].label ? tb[i].label : "",
768                                      -1, lpwsz, 128);
769         lpw   += nchar;
770         *lpw++ = 0;           // no creation data
771
772         /*-----------------------
773          * Define an edit control.
774          *-----------------------*/
775         lpw = lpwAlign (lpw); /* align DLGITEMTEMPLATE on DWORD boundary */
776         lpdit = (LPDLGITEMTEMPLATE) lpw;
777         lpdit->style = WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | WS_BORDER | (tb[i].echo == 1 ? 0L : ES_PASSWORD);
778         lpdit->dwExtendedStyle = 0;
779         lpdit->x  = 10 + (pwid + 1) * 4;
780         lpdit->y  = 10 + (numlines + i + 1) * 14;
781         lpdit->cx = (width - (pwid + 1)) * 4;
782         lpdit->cy = 14;
783         lpdit->id = ID_MID_TEXT + i;             // identifier
784
785         lpw = (LPWORD) (lpdit + 1);
786         *lpw++ = 0xFFFF;
787         *lpw++ = 0x0081;                         // edit class
788
789         lpwsz = (LPWSTR) lpw;
790         nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].def ? tb[i].def : "",
791                                      -1, lpwsz, 128);
792         lpw   += nchar;
793         *lpw++ = 0;           // no creation data
794     }
795
796     GlobalUnlock(hgbl);
797     ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,
798                                                         hwndOwner, (DLGPROC) MultiInputDialogProc);
799     GlobalFree(hgbl);
800
801     switch ( ret ) {
802     case 0:     /* Timeout */
803         return -1;
804     case IDOK:
805         return 1;
806     case IDCANCEL:
807         return 0;
808     default: {
809         char buf[256];
810         sprintf(buf,"DialogBoxIndirect() failed: %d",GetLastError());
811         MessageBox(hwndOwner,
812                     buf,
813                     "GetLastError()",
814                     MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
815         return -1;
816     }
817     }
818 }
819
820 static int
821 multi_field_dialog(HWND hParent, char * preface, int n, struct textField tb[])
822 {
823         extern HINSTANCE hLeashInst;
824     size_t maxwidth = 0;
825     int numlines = 0;
826     size_t len;
827     char * plines[16], *p = preface ? preface : "";
828     int i;
829
830     for ( i=0; i<16; i++ )
831         plines[i] = NULL;
832
833     while (*p && numlines < 16) {
834         plines[numlines++] = p;
835         for ( ;*p && *p != '\r' && *p != '\n'; p++ );
836         if ( *p == '\r' && *(p+1) == '\n' ) {
837             *p++ = '\0';
838             p++;
839         } else if ( *p == '\n' ) {
840             *p++ = '\0';
841         }
842         if ( strlen(plines[numlines-1]) > maxwidth )
843             maxwidth = strlen(plines[numlines-1]);
844     }
845
846     for ( i=0;i<n;i++ ) {
847         len = strlen(tb[i].label) + 1 + (tb[i].len > 40 ? 40 : tb[i].len);
848         if ( maxwidth < len )
849             maxwidth = len;
850     }
851
852     return(MultiInputDialog(hLeashInst, hParent, plines, numlines, maxwidth, n, tb));
853 }
854
855 static krb5_error_code KRB5_CALLCONV
856 leash_krb5_prompter( krb5_context context,
857                                          void *data,
858                                          const char *name,
859                                          const char *banner,
860                                          int num_prompts,
861                                          krb5_prompt prompts[])
862 {
863     krb5_error_code     errcode = 0;
864     int                 i;
865     struct textField * tb = NULL;
866     int    len = 0, blen=0, nlen=0;
867         HWND hParent = (HWND)data;
868
869     if (name)
870         nlen = strlen(name)+2;
871
872     if (banner)
873         blen = strlen(banner)+2;
874
875     tb = (struct textField *) malloc(sizeof(struct textField) * num_prompts);
876     if ( tb != NULL ) {
877         int ok;
878         memset(tb,0,sizeof(struct textField) * num_prompts);
879         for ( i=0; i < num_prompts; i++ ) {
880             tb[i].buf = prompts[i].reply->data;
881             tb[i].len = prompts[i].reply->length;
882             tb[i].label = prompts[i].prompt;
883             tb[i].def = NULL;
884             tb[i].echo = (prompts[i].hidden ? 2 : 1);
885         }
886
887         ok = multi_field_dialog(hParent,(char *)banner,num_prompts,tb);
888         if ( ok ) {
889             for ( i=0; i < num_prompts; i++ )
890                 prompts[i].reply->length = strlen(prompts[i].reply->data);
891         } else
892             errcode = -2;
893     }
894
895     if ( tb )
896         free(tb);
897     if (errcode) {
898         for (i = 0; i < num_prompts; i++) {
899             memset(prompts[i].reply->data, 0, prompts[i].reply->length);
900         }
901     }
902     return errcode;
903 }