1 // Module name: krb5routines.c
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.
11 #define _WIN32_WINNT 0x0501
13 #if _WIN32_WINNT < 0x0501
15 #define _WIN32_WINNT 0x0501
26 /* Private Include files */
29 #include "leash-int.h"
31 #define KRB5_DEFAULT_LIFE 60*60*10 /* 10 hours */
33 char *GetTicketFlag(krb5_creds *cred)
41 if (cred->ticket_flags & TKT_FLG_FORWARDABLE)
44 if (cred->ticket_flags & TKT_FLG_FORWARDED)
47 if (cred->ticket_flags & TKT_FLG_PROXIABLE)
50 if (cred->ticket_flags & TKT_FLG_PROXY)
53 if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE)
56 if (cred->ticket_flags & TKT_FLG_POSTDATED)
59 if (cred->ticket_flags & TKT_FLG_INVALID)
62 if (cred->ticket_flags & TKT_FLG_RENEWABLE)
65 if (cred->ticket_flags & TKT_FLG_INITIAL)
68 if (cred->ticket_flags & TKT_FLG_HW_AUTH)
71 if (cred->ticket_flags & TKT_FLG_PRE_AUTH)
86 krb5_error_code code = 0;
89 krb5_principal me = 0;
90 krb5_principal server = 0;
94 if ( !pkrb5_init_context )
97 memset(&my_creds, 0, sizeof(krb5_creds));
99 code = pkrb5_init_context(&ctx);
100 if (code) goto cleanup;
102 code = pkrb5_cc_default(ctx, &cc);
103 if (code) goto cleanup;
105 code = pkrb5_cc_get_principal(ctx, cc, &me);
106 if (code) goto cleanup;
108 realm = krb5_princ_realm(ctx, me);
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,
115 if ( code ) goto cleanup;
117 my_creds.client = me;
118 my_creds.server = server;
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);
124 if ( code != KRB5KDC_ERR_ETYPE_NOSUPP ||
125 code != KRB5_KDC_UNREACH)
126 Leash_krb5_error(code, "krb5_get_renewed_creds()", 0, &ctx, &cc);
130 code = pkrb5_cc_initialize(ctx, cc, me);
131 if (code) goto cleanup;
133 code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
134 if (code) goto cleanup;
137 if (my_creds.client == me)
139 if (my_creds.server == server)
141 pkrb5_free_cred_contents(ctx, &my_creds);
143 pkrb5_free_principal(ctx, me);
145 pkrb5_free_principal(ctx, server);
147 pkrb5_cc_close(ctx, cc);
149 pkrb5_free_context(ctx);
153 static krb5_error_code KRB5_CALLCONV
154 leash_krb5_prompter( krb5_context context,
159 krb5_prompt prompts[]);
163 krb5_context alt_ctx,
165 char *principal_name,
167 krb5_deltat lifetime,
170 krb5_deltat renew_life,
175 krb5_error_code code = 0;
176 krb5_context ctx = 0;
177 krb5_ccache cc = 0, defcache = 0;
178 krb5_principal me = 0;
181 krb5_get_init_creds_opt * options = NULL;
182 krb5_address ** addrs = NULL;
183 int i = 0, addr_count = 0;
185 const char * deftype = NULL;
187 if (!pkrb5_init_context)
190 memset(&my_creds, 0, sizeof(my_creds));
198 code = pkrb5_init_context(&ctx);
199 if (code) goto cleanup;
202 code = pkrb5_get_init_creds_opt_alloc(ctx, &options);
203 if (code) goto cleanup;
205 code = pkrb5_cc_default(ctx, &defcache);
206 if (code) goto cleanup;
208 code = pkrb5_parse_name(ctx, principal_name, &me);
209 if (code) goto cleanup;
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)
217 if (code == KRB5_CC_NOTFOUND) {
218 code = pkrb5_cc_new_unique(ctx, deftype, NULL, &cc);
223 pkrb5_cc_close(ctx, defcache);
228 code = pkrb5_unparse_name(ctx, me, &name);
229 if (code) goto cleanup;
232 lifetime = Leash_get_default_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,
245 pkrb5_get_init_creds_opt_set_renew_life(options,
248 pkrb5_get_init_creds_opt_set_address_list(options,NULL);
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;
257 pkrb5_os_localaddr(ctx, &local_addrs);
258 while ( local_addrs[i++] );
261 addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *));
263 pkrb5_free_addresses(ctx, local_addrs);
266 memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1));
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);
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);
284 memcpy(addrs[i]->contents,local_addrs[i]->contents,
285 local_addrs[i]->length); /* safe */
288 pkrb5_free_addresses(ctx, local_addrs);
290 addrs[i] = (krb5_address *)malloc(sizeof(krb5_address));
291 if (addrs[i] == NULL)
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)
301 netIPAddr = htonl(publicIP);
302 memcpy(addrs[i]->contents,&netIPAddr,4);
304 pkrb5_get_init_creds_opt_set_address_list(options,addrs);
309 code = pkrb5_get_init_creds_opt_set_out_ccache(ctx, options, cc);
313 code = pkrb5_get_init_creds_password(ctx,
316 password, // password
317 leash_krb5_prompter, // prompter
318 hParent, // prompter data
322 // @TODO: make this an option
323 if ((!code) && (cc != defcache)) {
324 code = pkrb5_cc_switch(ctx, cc);
326 const char *cctype = pkrb5_cc_get_type(ctx, cc);
327 if (cctype != NULL) {
329 sprintf_s(defname, sizeof(defname), "%s:", cctype);
330 pkrb5int_cc_user_set_default_name(ctx, defname);
335 if (code && cc_new) {
336 // don't leave newly-generated empty ccache lying around on failure
337 pkrb5_cc_destroy(ctx, cc);
341 for ( i=0;i<addr_count;i++ ) {
343 if ( addrs[i]->contents )
344 free(addrs[i]->contents);
349 if (my_creds.client == me)
351 pkrb5_free_cred_contents(ctx, &my_creds);
353 pkrb5_free_unparsed_name(ctx, name);
355 pkrb5_free_principal(ctx, me);
357 pkrb5_cc_close(ctx, cc);
359 pkrb5_get_init_creds_opt_free(ctx, options);
360 if (ctx && (ctx != alt_ctx))
361 pkrb5_free_context(ctx);
366 /**************************************/
367 /* LeashKRB5destroyTicket(): */
368 /**************************************/
380 rc = Leash_krb5_initialize(&ctx);
384 if (rc = pkrb5_cc_default(ctx, &cache))
387 rc = pkrb5_cc_destroy(ctx, cache);
390 pkrb5_free_context(ctx);
397 Leash_krb5_cc_default(krb5_context *ctx, krb5_ccache *cache)
402 char *functionName = NULL;
404 rc = pkrb5_cc_default(*ctx, cache);
406 functionName = "krb5_cc_default()";
410 flags = KRB5_TC_NOTICKET;
411 rc = pkrb5_cc_set_flags(*ctx, *cache, flags);
413 if (rc == KRB5_FCC_NOFILE || rc == KRB5_CC_NOTFOUND) {
414 if (*cache != NULL && *ctx != NULL)
415 pkrb5_cc_close(*ctx, *cache);
417 functionName = "krb5_cc_set_flags()";
422 if (rc && functionName) {
423 Leash_krb5_error(rc, functionName, 0, ctx, cache);
428 /**************************************/
429 /* Leash_krb5_initialize(): */
430 /**************************************/
431 int Leash_krb5_initialize(krb5_context *ctx)
433 LPCSTR functionName = NULL;
436 if (pkrb5_init_context == NULL)
440 if (rc = (*pkrb5_init_context)(ctx)) {
441 functionName = "krb5_init_context()";
442 return Leash_krb5_error(rc, functionName, 0, ctx, NULL);
449 /**************************************/
450 /* Leash_krb5_error(): */
451 /**************************************/
453 Leash_krb5_error(krb5_error_code rc, LPCSTR FailedFunctionName,
454 int FreeContextFlag, krb5_context * ctx,
457 #ifdef USE_MESSAGE_BOX
461 errText = perror_message(rc);
462 _snprintf(message, sizeof(message),
463 "%s\n(Kerberos error %ld)\n\n%s failed",
467 message[sizeof(message)-1] = 0;
469 MessageBox(NULL, message, "Kerberos Five", MB_OK | MB_ICONERROR |
472 #endif /* USE_MESSAGE_BOX */
474 if (ctx != NULL && *ctx != NULL) {
475 if (cache != NULL && *cache != NULL) {
476 pkrb5_cc_close(*ctx, *cache);
480 if (FreeContextFlag) {
481 pkrb5_free_context(*ctx);
491 Leash_ms2mit(BOOL save_creds)
493 krb5_context kcontext = 0;
494 krb5_error_code code;
495 krb5_ccache ccache=0;
496 krb5_ccache mslsa_ccache=0;
498 krb5_cc_cursor cursor=0;
499 krb5_principal princ = 0;
502 if ( !pkrb5_init_context )
505 if (code = pkrb5_init_context(&kcontext))
508 if (code = pkrb5_cc_resolve(kcontext, "MSLSA:", &mslsa_ccache))
512 if (code = pkrb5_cc_get_principal(kcontext, mslsa_ccache, &princ))
515 if (code = pkrb5_cc_default(kcontext, &ccache))
518 if (code = pkrb5_cc_initialize(kcontext, ccache, princ))
521 if (code = pkrb5_cc_copy_creds(kcontext, mslsa_ccache, ccache))
526 /* Enumerate tickets from cache looking for an initial ticket */
527 if ((code = pkrb5_cc_start_seq_get(kcontext, mslsa_ccache, &cursor)))
530 while (!(code = pkrb5_cc_next_cred(kcontext, mslsa_ccache, &cursor, &creds)))
532 if ( creds.ticket_flags & TKT_FLG_INITIAL ) {
534 pkrb5_free_cred_contents(kcontext, &creds);
537 pkrb5_free_cred_contents(kcontext, &creds);
539 pkrb5_cc_end_seq_get(kcontext, mslsa_ccache, &cursor);
544 pkrb5_free_principal(kcontext, princ);
546 pkrb5_cc_close(kcontext, ccache);
548 pkrb5_cc_close(kcontext, mslsa_ccache);
550 pkrb5_free_context(kcontext);
555 /* User Query data structures and functions */
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 */
565 static int mid_cnt = 0;
566 static struct textField * mid_tb = NULL;
569 #define ID_MID_TEXT 300
572 MultiInputDialogProc( HWND hDialog, UINT message, WPARAM wParam, LPARAM lParam)
578 if ( GetDlgCtrlID((HWND) wParam) != ID_MID_TEXT )
580 SetFocus(GetDlgItem( hDialog, ID_MID_TEXT));
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);
592 switch ( LOWORD(wParam) ) {
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';
600 EndDialog(hDialog, LOWORD(wParam));
608 lpwAlign( LPWORD lpIn )
620 * dialog widths are measured in 1/4 character widths
621 * dialog height are measured in 1/8 character heights
625 MultiInputDialog( HINSTANCE hinst, HWND hwndOwner,
626 char * ptext[], int numlines, int width,
627 int tb_cnt, struct textField * tb)
631 LPDLGITEMTEMPLATE lpdit;
638 hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
645 lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
647 // Define a dialog box.
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
656 lpdt->cx = 20 + width * 4;
657 lpdt->cy = 20 + (numlines + tb_cnt + 4) * 14;
659 lpw = (LPWORD) (lpdt + 1);
660 *lpw++ = 0; // no menu
661 *lpw++ = 0; // predefined dialog box class (by default)
663 lpwsz = (LPWSTR) lpw;
664 nchar = MultiByteToWideChar (CP_ACP, 0, "", -1, lpwsz, 128);
666 *lpw++ = 8; // font size (points)
667 lpwsz = (LPWSTR) lpw;
668 nchar = MultiByteToWideChar (CP_ACP, 0, "MS Shell Dlg",
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;
683 lpdit->id = IDOK; // OK button identifier
685 lpw = (LPWORD) (lpdit + 1);
687 *lpw++ = 0x0080; // button class
689 lpwsz = (LPWSTR) lpw;
690 nchar = MultiByteToWideChar (CP_ACP, 0, "OK", -1, lpwsz, 50);
692 *lpw++ = 0; // no creation data
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;
705 lpdit->id = IDCANCEL; // CANCEL button identifier
707 lpw = (LPWORD) (lpdit + 1);
709 *lpw++ = 0x0080; // button class
711 lpwsz = (LPWSTR) lpw;
712 nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
714 *lpw++ = 0; // no creation data
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;
726 lpdit->y = 10 + i * 14;
727 lpdit->cx = strlen(ptext[i]) * 4 + 10;
729 lpdit->id = ID_TEXT + i; // text identifier
731 lpw = (LPWORD) (lpdit + 1);
733 *lpw++ = 0x0082; // static class
735 lpwsz = (LPWSTR) lpw;
736 nchar = MultiByteToWideChar (CP_ACP, 0, ptext[i],
739 *lpw++ = 0; // no creation data
742 for ( i=0, pwid = 0; i<tb_cnt; i++) {
743 if ( pwid < strlen(tb[i].label) )
744 pwid = strlen(tb[i].label);
747 for ( i=0; i<tb_cnt; i++) {
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;
757 lpdit->y = 10 + (numlines + i + 1) * 14;
758 lpdit->cx = pwid * 4;
760 lpdit->id = ID_TEXT + numlines + i; // text identifier
762 lpw = (LPWORD) (lpdit + 1);
764 *lpw++ = 0x0082; // static class
766 lpwsz = (LPWSTR) lpw;
767 nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].label ? tb[i].label : "",
770 *lpw++ = 0; // no creation data
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;
783 lpdit->id = ID_MID_TEXT + i; // identifier
785 lpw = (LPWORD) (lpdit + 1);
787 *lpw++ = 0x0081; // edit class
789 lpwsz = (LPWSTR) lpw;
790 nchar = MultiByteToWideChar (CP_ACP, 0, tb[i].def ? tb[i].def : "",
793 *lpw++ = 0; // no creation data
797 ret = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,
798 hwndOwner, (DLGPROC) MultiInputDialogProc);
802 case 0: /* Timeout */
810 sprintf(buf,"DialogBoxIndirect() failed: %d",GetLastError());
811 MessageBox(hwndOwner,
814 MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
821 multi_field_dialog(HWND hParent, char * preface, int n, struct textField tb[])
823 extern HINSTANCE hLeashInst;
827 char * plines[16], *p = preface ? preface : "";
830 for ( i=0; i<16; i++ )
833 while (*p && numlines < 16) {
834 plines[numlines++] = p;
835 for ( ;*p && *p != '\r' && *p != '\n'; p++ );
836 if ( *p == '\r' && *(p+1) == '\n' ) {
839 } else if ( *p == '\n' ) {
842 if ( strlen(plines[numlines-1]) > maxwidth )
843 maxwidth = strlen(plines[numlines-1]);
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 )
852 return(MultiInputDialog(hLeashInst, hParent, plines, numlines, maxwidth, n, tb));
855 static krb5_error_code KRB5_CALLCONV
856 leash_krb5_prompter( krb5_context context,
861 krb5_prompt prompts[])
863 krb5_error_code errcode = 0;
865 struct textField * tb = NULL;
866 int len = 0, blen=0, nlen=0;
867 HWND hParent = (HWND)data;
870 nlen = strlen(name)+2;
873 blen = strlen(banner)+2;
875 tb = (struct textField *) malloc(sizeof(struct textField) * num_prompts);
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;
884 tb[i].echo = (prompts[i].hidden ? 2 : 1);
887 ok = multi_field_dialog(hParent,(char *)banner,num_prompts,tb);
889 for ( i=0; i < num_prompts; i++ )
890 prompts[i].reply->length = strlen(prompts[i].reply->data);
898 for (i = 0; i < num_prompts; i++) {
899 memset(prompts[i].reply->data, 0, prompts[i].reply->length);