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
GetTicketFlag(krb5_creds * cred)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
LeashKRB5_renew(void)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
Leash_krb5_kinit(krb5_context alt_ctx,HWND hParent,char * principal_name,char * password,krb5_deltat lifetime,DWORD forwardable,DWORD proxiable,krb5_deltat renew_life,DWORD addressless,DWORD publicIP)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
Leash_krb5_kdestroy(void)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
Leash_krb5_cc_default(krb5_context * ctx,krb5_ccache * cache)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 /**************************************/
Leash_krb5_initialize(krb5_context * ctx)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
Leash_krb5_error(krb5_error_code rc,LPCSTR FailedFunctionName,int FreeContextFlag,krb5_context * ctx,krb5_ccache * cache)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
MultiInputDialogProc(HWND hDialog,UINT message,WPARAM wParam,LPARAM lParam)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
lpwAlign(LPWORD lpIn)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
MultiInputDialog(HINSTANCE hinst,HWND hwndOwner,char * ptext[],int numlines,int width,int tb_cnt,struct textField * tb)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
multi_field_dialog(HWND hParent,char * preface,int n,struct textField tb[])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
leash_krb5_prompter(krb5_context context,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])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 }
839