b892531afc9b36f911837601e86817c9a4396651
[krb5.git] / src / windows / identity / plugins / krb5 / krb5util.c
1 /*\r
2  * Copyright (c) 2004 Massachusetts Institute of Technology\r
3  *\r
4  * Permission is hereby granted, free of charge, to any person\r
5  * obtaining a copy of this software and associated documentation\r
6  * files (the "Software"), to deal in the Software without\r
7  * restriction, including without limitation the rights to use, copy,\r
8  * modify, merge, publish, distribute, sublicense, and/or sell copies\r
9  * of the Software, and to permit persons to whom the Software is\r
10  * furnished to do so, subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be\r
13  * included in all copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
22  * SOFTWARE.\r
23  */\r
24 \r
25 /* $Id$ */\r
26 \r
27 #include <windows.h>\r
28 #include <stdio.h>\r
29 #include <sys/types.h>\r
30 #include <winsock.h>\r
31 #include "leashdll.h"\r
32 #include <KerberosIV/krb.h>\r
33 #include <prot.h>\r
34 #include <time.h>\r
35 \r
36 #include <leashwin.h>\r
37 #include "leasherr.h"\r
38 #include "leash-int.h"\r
39 #include "leashids.h"\r
40 \r
41 #include <mitwhich.h>\r
42 \r
43 #include <winkrbid.h>\r
44 #include "reminder.h"\r
45 \r
46 static char FAR *err_context;\r
47 \r
48 char KRB_HelpFile[_MAX_PATH] =  HELPFILE;\r
49 \r
50 #define LEN     64                /* Maximum Hostname Length */\r
51 \r
52 #define LIFE    DEFAULT_TKT_LIFE  /* lifetime of ticket in 5-minute units */\r
53 \r
54 char *\r
55 short_date(dp)\r
56     long   *dp;\r
57 {\r
58     register char *cp;\r
59     cp = ctime(dp) + 4; // skip day of week\r
60     // cp[15] = '\0';\r
61     cp[12] = '\0'; // Don't display seconds\r
62     return (cp);\r
63 }\r
64 \r
65 \r
66 static\r
67 char*\r
68 clean_string(\r
69     char* s\r
70     )\r
71 {\r
72     char* p = s;\r
73     char* b = s;\r
74 \r
75     if (!s) return s;\r
76 \r
77     for (p = s; *p; p++) {\r
78         switch (*p) {\r
79         case '\007':\r
80             /* Add more cases here */\r
81             break;\r
82         default:\r
83             *b = *p;\r
84             b++;\r
85         }\r
86     }\r
87     *b = *p;\r
88     return s;\r
89 }\r
90 \r
91 static\r
92 int\r
93 leash_error_message(\r
94     const char *error,\r
95     int rcL,\r
96     int rc4,\r
97     int rc5,\r
98     int rcA,\r
99     char* result_string,\r
100     int  displayMB\r
101     )\r
102 {\r
103     char message[2048];\r
104     char *p = message;\r
105     int size = sizeof(message);\r
106     int n;\r
107 \r
108     // XXX: ignore AFS for now.\r
109 \r
110     if (!rc5 && !rc4 && !rcL)\r
111         return 0;\r
112 \r
113     n = _snprintf(p, size, "%s\n\n", error);\r
114     p += n;\r
115     size -= n;\r
116 \r
117     if (rc5 && !result_string)\r
118     {\r
119         n = _snprintf(p, size,\r
120                       "Kerberos 5: %s (error %ld)\n",\r
121                       perror_message(rc5),\r
122                       rc5 & 255 // XXX: & 255??!!!\r
123             );\r
124         p += n;\r
125         size -= n;\r
126     }\r
127     if (rc4 && !result_string)\r
128     {\r
129         char buffer[1024];\r
130         n = _snprintf(p, size,\r
131                       "Kerberos 4: %s\n",\r
132                       err_describe(buffer, rc4)\r
133             );\r
134         p += n;\r
135         size -= n;\r
136     }\r
137     if (rcL)\r
138     {\r
139         char buffer[1024];\r
140         n = _snprintf(p, size,\r
141                       "\n%s\n",\r
142                       err_describe(buffer, rcL)\r
143             );\r
144         p += n;\r
145         size -= n;\r
146     }\r
147     if (result_string)\r
148     {\r
149         n = _snprintf(p, size,\r
150                       "%s\n",\r
151                       result_string);\r
152         p += n;\r
153         size -= n;\r
154     }\r
155     if ( displayMB )\r
156         MessageBox(NULL, message, "Leash", MB_OK | MB_ICONERROR | MB_TASKMODAL | \r
157                     MB_SETFOREGROUND);\r
158 \r
159     if (rc5) return rc5;\r
160     if (rc4) return rc4;\r
161     if (rcL) return rcL;\r
162     return 0;\r
163 }\r
164 \r
165 \r
166 static\r
167 char *\r
168 make_postfix(\r
169     const char * base,\r
170     const char * postfix,\r
171     char ** rcopy\r
172     )\r
173 {\r
174     int base_size;\r
175     int ret_size;\r
176     char * copy = 0;\r
177     char * ret = 0;\r
178 \r
179     base_size = strlen(base) + 1;\r
180     ret_size = base_size + strlen(postfix) + 1;\r
181     copy = malloc(base_size);\r
182     ret = malloc(ret_size);\r
183 \r
184     if (!copy || !ret)\r
185         goto cleanup;\r
186 \r
187     strncpy(copy, base, base_size);\r
188     copy[base_size - 1] = 0;\r
189 \r
190     strncpy(ret, base, base_size);\r
191     strncpy(ret + (base_size - 1), postfix, ret_size - (base_size - 1));\r
192     ret[ret_size - 1] = 0;\r
193 \r
194  cleanup:\r
195     if (!copy || !ret) {\r
196         if (copy)\r
197             free(copy);\r
198         if (ret)\r
199             free(ret);\r
200         copy = ret = 0;\r
201     }\r
202     // INVARIANT: (ret ==> copy) && (copy ==> ret)\r
203     *rcopy = copy;\r
204     return ret;\r
205 }\r
206 \r
207 static\r
208 long\r
209 make_temp_cache_v4(\r
210     const char * postfix\r
211     )\r
212 {\r
213     static char * old_cache = 0;\r
214 \r
215     if (!pkrb_set_tkt_string || !ptkt_string || !pdest_tkt)\r
216         return 0; // XXX - is this appropriate?\r
217 \r
218     if (old_cache) {\r
219         pdest_tkt();\r
220         pkrb_set_tkt_string(old_cache);\r
221         free(old_cache);\r
222         old_cache = 0;\r
223     }\r
224 \r
225     if (postfix)\r
226     {\r
227         char * tmp_cache = make_postfix(ptkt_string(), postfix, &old_cache);\r
228 \r
229         if (!tmp_cache)\r
230             return KFAILURE;\r
231 \r
232         pkrb_set_tkt_string(tmp_cache);\r
233         free(tmp_cache);\r
234     }\r
235     return 0;\r
236 }\r
237 \r
238 static\r
239 long\r
240 make_temp_cache_v5(\r
241     const char * postfix,\r
242     krb5_context * pctx\r
243     )\r
244 {\r
245     static krb5_context ctx = 0;\r
246     static char * old_cache = 0;\r
247 \r
248     // INVARIANT: old_cache ==> ctx && ctx ==> old_cache\r
249 \r
250     if (pctx)\r
251         *pctx = 0;\r
252 \r
253     if (!pkrb5_init_context || !pkrb5_free_context || !pkrb5_cc_resolve ||\r
254         !pkrb5_cc_default_name || !pkrb5_cc_set_default_name)\r
255         return 0;\r
256 \r
257     if (old_cache) {\r
258         krb5_ccache cc = 0;\r
259         if (!pkrb5_cc_resolve(ctx, pkrb5_cc_default_name(ctx), &cc))\r
260             pkrb5_cc_destroy(ctx, cc);\r
261         pkrb5_cc_set_default_name(ctx, old_cache);\r
262         free(old_cache);\r
263         old_cache = 0;\r
264     }\r
265     if (ctx) {\r
266         pkrb5_free_context(ctx);\r
267         ctx = 0;\r
268     }\r
269 \r
270     if (postfix)\r
271     {\r
272         char * tmp_cache = 0;\r
273         krb5_error_code rc = 0;\r
274 \r
275         rc = pkrb5_init_context(&ctx);\r
276         if (rc) goto cleanup;\r
277 \r
278         tmp_cache = make_postfix(pkrb5_cc_default_name(ctx), postfix, \r
279                                  &old_cache);\r
280 \r
281         if (!tmp_cache) {\r
282             rc = ENOMEM;\r
283             goto cleanup;\r
284         }\r
285 \r
286         rc = pkrb5_cc_set_default_name(ctx, tmp_cache);\r
287 \r
288     cleanup:\r
289         if (rc && ctx) {\r
290             pkrb5_free_context(ctx);\r
291             ctx = 0;\r
292         }\r
293         if (tmp_cache)\r
294             free(tmp_cache);\r
295         if (pctx)\r
296             *pctx = ctx;\r
297         return rc;\r
298     }\r
299     return 0;\r
300 }\r
301 \r
302 long\r
303 Leash_checkpwd(\r
304     char *principal, \r
305     char *password\r
306     )\r
307 {\r
308     return Leash_int_checkpwd(principal, password, 0);\r
309 }\r
310 \r
311 long \r
312 Leash_int_checkpwd(\r
313     char * principal,\r
314     char * password,\r
315     int    displayErrors\r
316     )\r
317 {\r
318     long rc = 0;\r
319         krb5_context ctx = 0;   // statically allocated in make_temp_cache_v5\r
320     // XXX - we ignore errors in make_temp_cache_v?  This is BAD!!!\r
321     make_temp_cache_v4("_checkpwd");\r
322     make_temp_cache_v5("_checkpwd", &ctx);\r
323     rc = Leash_int_kinit_ex( ctx, 0,\r
324                              principal, password, 0, 0, 0, 0,\r
325                              Leash_get_default_noaddresses(),\r
326                              Leash_get_default_publicip(),\r
327                              displayErrors\r
328                              );\r
329     make_temp_cache_v4(0);\r
330     make_temp_cache_v5(0, &ctx);\r
331     return rc;\r
332 }\r
333 \r
334 static\r
335 long\r
336 Leash_changepwd_v5(char * principal,\r
337                    char * password,\r
338                    char * newpassword,\r
339                    char** error_str)\r
340 {\r
341     krb5_error_code rc = 0;\r
342     int result_code;\r
343     krb5_data result_code_string, result_string;\r
344     krb5_context context = 0;\r
345     krb5_principal princ = 0;\r
346     krb5_get_init_creds_opt opts;\r
347     krb5_creds creds;\r
348     DWORD addressless = 0;\r
349 \r
350     result_string.data = 0;\r
351     result_code_string.data = 0;\r
352 \r
353     if ( !pkrb5_init_context )\r
354         goto cleanup;\r
355 \r
356    if (rc = pkrb5_init_context(&context)) {\r
357 #if 0\r
358        com_err(argv[0], ret, "initializing kerberos library");\r
359 #endif\r
360        goto cleanup;\r
361    }\r
362 \r
363    if (rc = pkrb5_parse_name(context, principal, &princ)) {\r
364 #if 0\r
365        com_err(argv[0], ret, "parsing client name");\r
366 #endif\r
367        goto cleanup;\r
368    }\r
369 \r
370    pkrb5_get_init_creds_opt_init(&opts);\r
371    pkrb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);\r
372    pkrb5_get_init_creds_opt_set_renew_life(&opts, 0);\r
373    pkrb5_get_init_creds_opt_set_forwardable(&opts, 0);\r
374    pkrb5_get_init_creds_opt_set_proxiable(&opts, 0);\r
375 \r
376    addressless = Leash_get_default_noaddresses();\r
377    if (addressless)\r
378        pkrb5_get_init_creds_opt_set_address_list(&opts,NULL);\r
379 \r
380 \r
381    if (rc = pkrb5_get_init_creds_password(context, &creds, princ, password,\r
382                                           0, 0, 0, "kadmin/changepw", &opts)) {\r
383        if (rc == KRB5KRB_AP_ERR_BAD_INTEGRITY) {\r
384 #if 0\r
385            com_err(argv[0], 0,\r
386                    "Password incorrect while getting initial ticket");\r
387 #endif\r
388        }\r
389        else {\r
390 #if 0\r
391            com_err(argv[0], ret, "getting initial ticket");\r
392 #endif\r
393        }\r
394        goto cleanup;\r
395    }\r
396 \r
397    if (rc = pkrb5_change_password(context, &creds, newpassword,\r
398                                   &result_code, &result_code_string,\r
399                                   &result_string)) {\r
400 #if 0\r
401        com_err(argv[0], ret, "changing password");\r
402 #endif\r
403        goto cleanup;\r
404    }\r
405 \r
406    if (result_code) {\r
407        int len = result_code_string.length + \r
408            (result_string.length ? (sizeof(": ") - 1) : 0) +\r
409            result_string.length;\r
410        if (len && error_str) {\r
411            *error_str = malloc(len + 1);\r
412            if (*error_str)\r
413                _snprintf(*error_str, len + 1,\r
414                          "%.*s%s%.*s",\r
415                          result_code_string.length, result_code_string.data,\r
416                          result_string.length?": ":"",\r
417                          result_string.length, result_string.data);\r
418        }\r
419       rc = result_code;\r
420       goto cleanup;\r
421    }\r
422 \r
423  cleanup:\r
424    if (result_string.data)\r
425        pkrb5_free_data_contents(context, &result_string);\r
426 \r
427    if (result_code_string.data)\r
428        pkrb5_free_data_contents(context, &result_code_string);\r
429 \r
430    if (princ)\r
431        pkrb5_free_principal(context, princ);\r
432 \r
433    if (context)\r
434        pkrb5_free_context(context);\r
435 \r
436    return rc;\r
437 }\r
438 \r
439 static\r
440 long\r
441 Leash_changepwd_v4(\r
442     char * principal,\r
443     char * password,\r
444     char * newpassword,\r
445     char** error_str\r
446     )\r
447 {\r
448     long k_errno;\r
449 \r
450     if (!pkrb_set_tkt_string || !ptkt_string || !pkadm_change_your_password ||\r
451         !pdest_tkt)\r
452         return KFAILURE;\r
453 \r
454     k_errno = make_temp_cache_v4("_chgpwd");\r
455     if (k_errno) return k_errno;\r
456     k_errno = pkadm_change_your_password(principal, password, newpassword, \r
457                                          error_str);\r
458     make_temp_cache_v4(0);\r
459     return k_errno;\r
460 }\r
461 \r
462 /*\r
463  * Leash_changepwd\r
464  *\r
465  * Try to change the password using one of krb5 or krb4 -- whichever one\r
466  * works.  We return ok on the first one that works.\r
467  */\r
468 long\r
469 Leash_changepwd(\r
470     char * principal, \r
471     char * password, \r
472     char * newpassword,\r
473     char** result_string\r
474     )\r
475 {\r
476     return Leash_int_changepwd(principal, password, newpassword, result_string, 0);\r
477 }\r
478 \r
479 long\r
480 Leash_int_changepwd(\r
481     char * principal, \r
482     char * password, \r
483     char * newpassword,\r
484     char** result_string,\r
485     int    displayErrors\r
486     )\r
487 {\r
488     char* v5_error_str = 0;\r
489     char* v4_error_str = 0;\r
490     char* error_str = 0;\r
491     int rc4 = 0;\r
492     int rc5 = 0;\r
493     int rc = 0;\r
494     if (hKrb5)\r
495         rc = rc5 = Leash_changepwd_v5(principal, password, newpassword,\r
496                                       &v5_error_str);\r
497     if (hKrb4 && \r
498                 Leash_get_default_use_krb4() &&\r
499             (!hKrb5 || rc5))\r
500         rc = rc4 = Leash_changepwd_v4(principal, password, newpassword, \r
501                                       &v4_error_str);\r
502     if (!rc)\r
503         return 0;\r
504     if (v5_error_str || v4_error_str) {\r
505         int len = 0;\r
506         char v5_prefix[] = "Kerberos 5: ";\r
507         char sep[] = "\n";\r
508         char v4_prefix[] = "Kerberos 4: ";\r
509 \r
510         clean_string(v5_error_str);\r
511         clean_string(v4_error_str);\r
512 \r
513         if (v5_error_str)\r
514             len += sizeof(sep) + sizeof(v5_prefix) + strlen(v5_error_str) + \r
515                 sizeof(sep);\r
516         if (v4_error_str)\r
517             len += sizeof(sep) + sizeof(v4_prefix) + strlen(v4_error_str) + \r
518                 sizeof(sep);\r
519         error_str = malloc(len + 1);\r
520         if (error_str) {\r
521             char* p = error_str;\r
522             int size = len + 1;\r
523             int n;\r
524             if (v5_error_str) {\r
525                 n = _snprintf(p, size, "%s%s%s%s",\r
526                               sep, v5_prefix, v5_error_str, sep);\r
527                 p += n;\r
528                 size -= n;\r
529             }\r
530             if (v4_error_str) {\r
531                 n = _snprintf(p, size, "%s%s%s%s",\r
532                               sep, v4_prefix, v4_error_str, sep);\r
533                 p += n;\r
534                 size -= n;\r
535             }\r
536             if (result_string)\r
537                 *result_string = error_str;\r
538         }\r
539     }\r
540     return leash_error_message("Error while changing password.", \r
541                                rc4, rc4, rc5, 0, error_str, \r
542                                displayErrors\r
543                                );\r
544 }\r
545 \r
546 int (*Lcom_err)(LPSTR,long,LPSTR,...);\r
547 LPSTR (*Lerror_message)(long);\r
548 LPSTR (*Lerror_table_name)(long);\r
549 \r
550 \r
551 long\r
552 Leash_kinit(\r
553     char * principal,\r
554     char * password,\r
555     int lifetime\r
556     )\r
557 {\r
558     return Leash_int_kinit_ex( 0, 0,\r
559                                principal, \r
560                                password, \r
561                                lifetime,\r
562                                Leash_get_default_forwardable(),\r
563                                Leash_get_default_proxiable(),\r
564                                Leash_get_default_renew_till(),\r
565                                Leash_get_default_noaddresses(),\r
566                                Leash_get_default_publicip(),\r
567                                0\r
568                                );\r
569 }\r
570 \r
571 long\r
572 Leash_kinit_ex(\r
573     char * principal, \r
574     char * password, \r
575     int lifetime,\r
576     int forwardable,\r
577     int proxiable,\r
578     int renew_life,\r
579     int addressless,\r
580     unsigned long publicip\r
581     )\r
582 {\r
583     return Leash_int_kinit_ex( 0, /* krb5 context */\r
584                                0, /* parent window */\r
585                                principal, \r
586                                password, \r
587                                lifetime,\r
588                                forwardable,\r
589                                proxiable,\r
590                                renew_life,\r
591                                addressless,\r
592                                publicip,\r
593                                0\r
594                                );\r
595 }\r
596 \r
597 long\r
598 Leash_int_kinit_ex(\r
599     krb5_context ctx,\r
600     HWND hParent,\r
601     char * principal, \r
602     char * password, \r
603     int lifetime,\r
604     int forwardable,\r
605     int proxiable,\r
606     int renew_life,\r
607     int addressless,\r
608     unsigned long publicip,\r
609     int displayErrors\r
610     )\r
611 {\r
612     LPCSTR  functionName; \r
613     char    aname[ANAME_SZ];\r
614     char    inst[INST_SZ];\r
615     char    realm[REALM_SZ];\r
616     char    first_part[256];\r
617     char    second_part[256];\r
618     char    temp[1024];\r
619     int     count;\r
620     int     i;\r
621     int rc4 = 0;\r
622     int rc5 = 0;\r
623     int rcA = 0;\r
624     int rcL = 0;\r
625 \r
626     if (lifetime < 5)\r
627         lifetime = 1;\r
628     else\r
629         lifetime /= 5;\r
630 \r
631         if (renew_life > 0 && renew_life < 5)\r
632                 renew_life = 1;\r
633         else\r
634                 renew_life /= 5;\r
635 \r
636     /* This should be changed if the maximum ticket lifetime */\r
637     /* changes */\r
638 \r
639     if (lifetime > 255)\r
640         lifetime = 255;\r
641 \r
642     err_context = "parsing principal";\r
643 \r
644     memset(temp, '\0', sizeof(temp));\r
645     memset(inst, '\0', sizeof(inst));\r
646     memset(realm, '\0', sizeof(realm));\r
647     memset(first_part, '\0', sizeof(first_part));\r
648     memset(second_part, '\0', sizeof(second_part));\r
649 \r
650     sscanf(principal, "%[/0-9a-zA-Z._-]@%[/0-9a-zA-Z._-]", first_part, second_part);\r
651     strcpy(temp, first_part);\r
652     strcpy(realm, second_part);\r
653     memset(first_part, '\0', sizeof(first_part));\r
654     memset(second_part, '\0', sizeof(second_part));\r
655     if (sscanf(temp, "%[@0-9a-zA-Z._-]/%[@0-9a-zA-Z._-]", first_part, second_part) == 2)\r
656     {\r
657         strcpy(aname, first_part);\r
658         strcpy(inst, second_part);\r
659     }\r
660     else\r
661     {\r
662         count = 0;\r
663         i = 0;\r
664         for (i = 0; temp[i]; i++)\r
665         {\r
666             if (temp[i] == '.')\r
667                 ++count;\r
668         }\r
669         if (count > 1)\r
670         {\r
671             strcpy(aname, temp);\r
672         }\r
673         else\r
674         {\r
675             if (pkname_parse != NULL)\r
676             {\r
677                 memset(first_part, '\0', sizeof(first_part));\r
678                 memset(second_part, '\0', sizeof(second_part));\r
679                 sscanf(temp, "%[@/0-9a-zA-Z_-].%[@/0-9a-zA-Z_-]", first_part, second_part);\r
680                 strcpy(aname, first_part);\r
681                 strcpy(inst, second_part);\r
682             }\r
683             else\r
684             {\r
685                 strcpy(aname, temp);\r
686             }\r
687         }\r
688     }\r
689 \r
690     memset(temp, '\0', sizeof(temp));\r
691     strcpy(temp, aname);\r
692     if (strlen(inst) != 0)\r
693     {\r
694         strcat(temp, "/");\r
695         strcat(temp, inst);\r
696     }\r
697     if (strlen(realm) != 0)\r
698     {\r
699         strcat(temp, "@");\r
700         strcat(temp, realm);\r
701     }\r
702 \r
703     rc5 = Leash_krb5_kinit(ctx, hParent, \r
704                                                         temp, password, lifetime,\r
705                                                         forwardable,\r
706                                                         proxiable,\r
707                                                         renew_life,\r
708                                                         addressless,\r
709                                                         publicip\r
710                                                         );\r
711         if ( Leash_get_default_use_krb4() ) {\r
712                 if ( !rc5 ) {\r
713             if (!Leash_convert524(ctx))\r
714                 rc4 = KFAILURE;\r
715                 } else {\r
716                         if (pkname_parse == NULL)\r
717                         {\r
718                                 goto cleanup;\r
719                         }\r
720 \r
721                         err_context = "getting realm";\r
722                         if (!*realm && (rc4  = (int)(*pkrb_get_lrealm)(realm, 1))) \r
723                         {\r
724                                 functionName = "krb_get_lrealm()";\r
725                                 rcL  = LSH_FAILEDREALM;\r
726                                 goto cleanup;\r
727                         }\r
728 \r
729                         err_context = "checking principal";\r
730                         if ((!*aname) || (!(rc4  = (int)(*pk_isname)(aname))))\r
731                         {\r
732                                 functionName = "krb_get_lrealm()";\r
733                                 rcL = LSH_INVPRINCIPAL;\r
734                                 goto cleanup;\r
735                         }\r
736 \r
737                         /* optional instance */\r
738                         if (!(rc4 = (int)(*pk_isinst)(inst)))\r
739                         {\r
740                                 functionName = "k_isinst()";\r
741                                 rcL = LSH_INVINSTANCE;\r
742                                 goto cleanup;\r
743                         }\r
744 \r
745                         if (!(rc4 = (int)(*pk_isrealm)(realm)))\r
746                         {\r
747                                 functionName = "k_isrealm()";\r
748                                 rcL = LSH_INVREALM;\r
749                                 goto cleanup;\r
750                         }\r
751 \r
752                         err_context = "fetching ticket";        \r
753                         rc4 = (*pkrb_get_pw_in_tkt)(aname, inst, realm, "krbtgt", realm, \r
754                                                                                            lifetime, password);\r
755                         if (rc4) /* XXX: do we want: && (rc != NO_TKT_FIL) as well? */\r
756                         { \r
757                                 functionName = "krb_get_pw_in_tkt()";\r
758                                 rcL = KRBERR(rc4);\r
759                                 goto cleanup;\r
760                         }\r
761                 }\r
762         }\r
763 \r
764 #ifndef NO_AFS\r
765     if ( !rc5 || (Leash_get_default_use_krb4() && !rc4) ) {\r
766         char c;\r
767         char *r;\r
768         char *t;\r
769         for ( r=realm, t=temp; c=*r; r++,t++ )\r
770             *t = isupper(c) ? tolower(c) : c;\r
771         *t = '\0';\r
772 \r
773         rcA = Leash_afs_klog("afs", temp, realm, lifetime);\r
774         if (rcA)\r
775             rcA = Leash_afs_klog("afs", "", realm, lifetime);\r
776     }\r
777 #endif /* NO_AFS */\r
778 \r
779  cleanup:\r
780     return leash_error_message("Ticket initialization failed.", \r
781                                rcL, (rc5 && rc4)?KRBERR(rc4):0, rc5, rcA, 0,\r
782                                displayErrors);\r
783 }\r
784 \r
785 long FAR\r
786 Leash_renew(void)\r
787 {\r
788     if ( hKrb5 && !LeashKRB5_renew() ) {\r
789         int lifetime;\r
790         lifetime = Leash_get_default_lifetime() / 5;\r
791                 if (hKrb4 && Leash_get_default_use_krb4())\r
792                         Leash_convert524(0);\r
793 #ifndef NO_AFS\r
794         {\r
795             TicketList * list = NULL, * token;\r
796             afs_get_tokens(NULL,&list,NULL);\r
797             for ( token = list ; token ; token = token->next )\r
798                 Leash_afs_klog("afs", token->realm, "", lifetime);\r
799             not_an_API_LeashFreeTicketList(&list);\r
800         }\r
801 #endif /* NO_AFS */\r
802         return 1;\r
803     }\r
804     return 0;\r
805 }\r
806 \r
807 static BOOL\r
808 GetSecurityLogonSessionData(PSECURITY_LOGON_SESSION_DATA * ppSessionData)\r
809 {\r
810     NTSTATUS Status = 0;\r
811     HANDLE  TokenHandle;\r
812     TOKEN_STATISTICS Stats;\r
813     DWORD   ReqLen;\r
814     BOOL    Success;\r
815 \r
816     if (!ppSessionData || !pLsaGetLogonSessionData)\r
817         return FALSE;\r
818     *ppSessionData = NULL;\r
819 \r
820     Success = OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &TokenHandle );\r
821     if ( !Success )\r
822         return FALSE;\r
823 \r
824     Success = GetTokenInformation( TokenHandle, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );\r
825     CloseHandle( TokenHandle );\r
826     if ( !Success )\r
827         return FALSE;\r
828 \r
829     Status = pLsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );\r
830     if ( FAILED(Status) || !ppSessionData )\r
831         return FALSE;\r
832 \r
833     return TRUE;\r
834 }\r
835 \r
836 // IsKerberosLogon() does not validate whether or not there are valid tickets in the \r
837 // cache.  It validates whether or not it is reasonable to assume that if we \r
838 // attempted to retrieve valid tickets we could do so.  Microsoft does not \r
839 // automatically renew expired tickets.  Therefore, the cache could contain\r
840 // expired or invalid tickets.  Microsoft also caches the user's password \r
841 // and will use it to retrieve new TGTs if the cache is empty and tickets\r
842 // are requested.\r
843 \r
844 static BOOL\r
845 IsKerberosLogon(VOID)\r
846 {\r
847     PSECURITY_LOGON_SESSION_DATA pSessionData = NULL;\r
848     BOOL    Success = FALSE;\r
849 \r
850     if ( GetSecurityLogonSessionData(&pSessionData) ) {\r
851         if ( pSessionData->AuthenticationPackage.Buffer ) {\r
852             WCHAR buffer[256];\r
853             WCHAR *usBuffer;\r
854             int usLength;\r
855 \r
856             Success = FALSE;\r
857             usBuffer = (pSessionData->AuthenticationPackage).Buffer;\r
858             usLength = (pSessionData->AuthenticationPackage).Length;\r
859             if (usLength < 256)\r
860             {\r
861                 lstrcpyn (buffer, usBuffer, usLength);\r
862                 lstrcat (buffer,L"");\r
863                 if ( !lstrcmp(L"Kerberos",buffer) )\r
864                     Success = TRUE;\r
865             }\r
866         }\r
867         pLsaFreeReturnBuffer(pSessionData);\r
868     }\r
869     return Success;\r
870 }\r
871 \r
872 \r
873 // This looks really ugly because it is.  The result of IsKerberosLogon()\r
874 // does not prove whether or not there are Kerberos tickets available to \r
875 // be imported.  Only the call to khm_krb5_ms2mit() which actually attempts\r
876 // to import tickets can do that.  However, calling khm_krb5_ms2mit() can\r
877 // result in a TGS_REQ being sent to the KDC and since Leash_importable()\r
878 // is called quite often we want to avoid this if at all possible.\r
879 // Unfortunately, we have be shown at least one case in which the primary\r
880 // authentication package was not Kerberos and yet there were Kerberos \r
881 // tickets available.  Therefore, if IsKerberosLogon() is not TRUE we \r
882 // must call khm_krb5_ms2mit() but we still do not want to call it in a \r
883 // tight loop so we cache the response and assume it won't change.\r
884 long FAR\r
885 Leash_importable(void)\r
886 {\r
887     if ( IsKerberosLogon() )\r
888         return TRUE;\r
889     else {\r
890         static int response = -1;\r
891         if (response == -1) {\r
892             response = khm_krb5_ms2mit(0);\r
893         }\r
894         return response;\r
895     }\r
896 }\r
897 \r
898 long FAR\r
899 Leash_import(void)\r
900 {\r
901     if ( khm_krb5_ms2mit(1) ) {\r
902         int lifetime;\r
903         lifetime = Leash_get_default_lifetime() / 5;\r
904                 if (hKrb4 && Leash_get_default_use_krb4())\r
905                         Leash_convert524(0);\r
906 #ifndef NO_AFS\r
907         {\r
908             char c;\r
909             char *r;\r
910             char *t;\r
911             char  cell[256];\r
912             char  realm[256];\r
913             int   i = 0;\r
914             int   rcA = 0;\r
915 \r
916             krb5_context ctx = 0;\r
917             krb5_error_code code = 0;\r
918             krb5_ccache cc = 0;\r
919             krb5_principal me = 0;\r
920 \r
921             if ( !pkrb5_init_context )\r
922                 goto cleanup;\r
923 \r
924             code = pkrb5_init_context(&ctx);\r
925             if (code) goto cleanup;\r
926 \r
927             code = pkrb5_cc_default(ctx, &cc);\r
928             if (code) goto cleanup;\r
929 \r
930             if (code = pkrb5_cc_get_principal(ctx, cc, &me))\r
931                 goto cleanup;\r
932 \r
933             for ( r=realm, t=cell, i=0; i<krb5_princ_realm(ctx, me)->length; r++,t++,i++ ) {\r
934                 c = krb5_princ_realm(ctx, me)->data[i];\r
935                 *r = c;\r
936                 *t = isupper(c) ? tolower(c) : c;\r
937             }\r
938             *r = *t = '\0';\r
939 \r
940             rcA = Leash_afs_klog("afs", cell, realm, lifetime);\r
941             if (rcA)\r
942                 rcA = Leash_afs_klog("afs", "", realm, lifetime);\r
943 \r
944           cleanup:\r
945             if (me) \r
946                 pkrb5_free_principal(ctx, me);\r
947             if (cc)\r
948                 pkrb5_cc_close(ctx, cc);\r
949             if (ctx) \r
950                 pkrb5_free_context(ctx);\r
951         }\r
952 #endif /* NO_AFS */\r
953         return 1;\r
954     }\r
955     return 0;\r
956 }\r
957 \r
958 long\r
959 Leash_kdestroy(void)\r
960 {\r
961     int k_errno;\r
962 \r
963     Leash_afs_unlog();\r
964     khm_krb5_destroy_identity(NULL);\r
965 \r
966     if (pdest_tkt != NULL)\r
967     {\r
968         k_errno = (*pdest_tkt)();\r
969         if (k_errno && (k_errno != RET_TKFIL))\r
970             return KRBERR(k_errno);\r
971     }\r
972 \r
973     return 0;\r
974 }\r
975 \r
976 int com_addr(void)\r
977 {\r
978     long ipAddr;\r
979     char loc_addr[ADDR_SZ];\r
980     CREDENTIALS cred;    \r
981     char service[40];\r
982     char instance[40];\r
983 //    char addr[40];\r
984     char realm[40];\r
985     struct in_addr LocAddr;\r
986     int k_errno;\r
987 \r
988     if (pkrb_get_cred == NULL)\r
989         return(KSUCCESS);\r
990 \r
991     k_errno = (*pkrb_get_cred)(service,instance,realm,&cred);\r
992     if (k_errno)\r
993         return KRBERR(k_errno);\r
994 \r
995 \r
996     while(1) {\r
997         ipAddr = (*pLocalHostAddr)();\r
998         LocAddr.s_addr = ipAddr;\r
999         strcpy(loc_addr,inet_ntoa(LocAddr));\r
1000         if ( strcmp(cred.address,loc_addr) != 0) {\r
1001             Leash_kdestroy ();\r
1002             break;\r
1003         }\r
1004         break;\r
1005     } // while()\r
1006     return 0;\r
1007\r
1008 \r
1009 long FAR\r
1010 not_an_API_LeashFreeTicketList(TicketList** ticketList) \r
1011 {\r
1012     TicketList* tempList = *ticketList, *killList; \r
1013 \r
1014     //if (tempList == NULL)\r
1015     //return -1;\r
1016 \r
1017     while (tempList)\r
1018     {\r
1019         killList = tempList;\r
1020            \r
1021         tempList = (TicketList*)tempList->next;\r
1022         free(killList->theTicket);\r
1023         if (killList->tktEncType)\r
1024             free(killList->tktEncType);\r
1025         if (killList->keyEncType)\r
1026             free(killList->keyEncType);\r
1027         if (killList->addrCount) {\r
1028             int n;\r
1029             for ( n=0; n<killList->addrCount; n++) {\r
1030                 if (killList->addrList[n])\r
1031                     free(killList->addrList[n]);\r
1032             }\r
1033         }\r
1034         if (killList->addrList)\r
1035             free(killList->addrList);\r
1036         if (killList->name)\r
1037             free(killList->name);\r
1038         if (killList->inst)\r
1039             free(killList->inst);\r
1040         if (killList->realm)\r
1041             free(killList->realm);\r
1042         free(killList);\r
1043     }\r
1044 \r
1045     *ticketList = NULL;\r
1046     return 0;\r
1047 }\r
1048 \r
1049 \r
1050 long FAR Leash_klist(HWND hlist, TICKETINFO FAR *ticketinfo)\r
1051 {\r
1052     // Don't think this function will be used anymore - ADL 5-15-99    \r
1053     // Old fucntion to put tickets in a listbox control  \r
1054     // Use function  "not_an_API_LeashKRB4GetTickets()" instead! \r
1055     char    pname[ANAME_SZ];\r
1056     char    pinst[INST_SZ];\r
1057     char    prealm[REALM_SZ];\r
1058     char    buf[MAX_K_NAME_SZ+40];\r
1059     LPSTR   cp;\r
1060     long    expdate;\r
1061     int     k_errno;\r
1062     CREDENTIALS c;\r
1063     int newtickets = 0;\r
1064     int open = 0;\r
1065 \r
1066     /*\r
1067      * Since krb_get_tf_realm will return a ticket_file error,\r
1068      * we will call tf_init and tf_close first to filter out\r
1069      * things like no ticket file.  Otherwise, the error that\r
1070      * the user would see would be \r
1071      * klist: can't find realm of ticket file: No ticket file (tf_util)\r
1072      * instead of\r
1073      * klist: No ticket file (tf_util)\r
1074      */\r
1075     if (ptf_init == NULL)\r
1076         return(KSUCCESS);\r
1077 \r
1078     if (hlist) \r
1079     { \r
1080         SendMessage(hlist, WM_SETREDRAW, FALSE, 0L);\r
1081         SendMessage(hlist, LB_RESETCONTENT, 0, 0L);\r
1082     }                              \r
1083     com_addr();                    \r
1084     newtickets = NO_TICKETS;\r
1085 \r
1086     err_context = (LPSTR)"tktf1";\r
1087 \r
1088     /* Open ticket file */\r
1089     if (k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL))\r
1090     {\r
1091         goto cleanup;\r
1092     }\r
1093     /* Close ticket file */\r
1094     (void) (*ptf_close)();\r
1095     /*\r
1096      * We must find the realm of the ticket file here before calling\r
1097      * tf_init because since the realm of the ticket file is not\r
1098      * really stored in the principal section of the file, the\r
1099      * routine we use must itself call tf_init and tf_close.\r
1100      */\r
1101     err_context = "tf realm";\r
1102     if ((k_errno = (*pkrb_get_tf_realm)((*ptkt_string)(), prealm)) != KSUCCESS)\r
1103     {\r
1104         goto cleanup;\r
1105     }\r
1106     /* Open ticket file */\r
1107     err_context = "tf init";\r
1108     if (k_errno = (*ptf_init)((*ptkt_string)(), R_TKT_FIL)) \r
1109     {\r
1110         goto cleanup;                            \r
1111     }\r
1112 \r
1113     open = 1;\r
1114     err_context = "tf pname";\r
1115     /* Get principal name and instance */\r
1116     if ((k_errno = (*ptf_get_pname)(pname)) || (k_errno = (*ptf_get_pinst)(pinst))) \r
1117     {\r
1118         goto cleanup;             \r
1119     }\r
1120 \r
1121     /*\r
1122      * You may think that this is the obvious place to get the\r
1123      * realm of the ticket file, but it can't be done here as the\r
1124      * routine to do this must open the ticket file.  This is why\r
1125      * it was done before tf_init.\r
1126      */\r
1127 \r
1128     wsprintf((LPSTR)ticketinfo->principal,"%s%s%s%s%s", (LPSTR)pname,\r
1129              (LPSTR)(pinst[0] ? "." : ""), (LPSTR)pinst,\r
1130              (LPSTR)(prealm[0] ? "@" : ""), (LPSTR)prealm);\r
1131     newtickets = GOOD_TICKETS;\r
1132 \r
1133     err_context = "tf cred";\r
1134     while ((k_errno = (*ptf_get_cred)(&c)) == KSUCCESS) \r
1135     {\r
1136         expdate = c.issue_date + c.lifetime * 5L * 60L;\r
1137 \r
1138         if (!lstrcmp((LPSTR)c.service, (LPSTR)TICKET_GRANTING_TICKET) && !lstrcmp((LPSTR)c.instance, (LPSTR)prealm)) \r
1139         {\r
1140             ticketinfo->issue_date = c.issue_date;\r
1141             ticketinfo->lifetime = c.lifetime * 5L * 60L;\r
1142             ticketinfo->renew_till = 0;\r
1143         }\r
1144 \r
1145         cp = (LPSTR)buf;\r
1146         lstrcpy(cp, (LPSTR)short_date(&c.issue_date));\r
1147         cp += lstrlen(cp);\r
1148         wsprintf(cp,"\t%s\t%s%s%s%s%s",\r
1149                  (LPSTR)short_date(&expdate), (LPSTR)c.service,\r
1150                  (LPSTR)(c.instance[0] ? "." : ""),\r
1151                  (LPSTR)c.instance, (LPSTR)(c.realm[0] ? "@" : ""),\r
1152                  (LPSTR) c.realm);\r
1153         if (hlist)\r
1154             SendMessage(hlist, LB_ADDSTRING, 0, (LONG)(LPSTR)buf);\r
1155     } /* WHILE */\r
1156 \r
1157 cleanup:\r
1158 \r
1159     if (open)\r
1160         (*ptf_close)(); /* close ticket file */\r
1161 \r
1162     if (hlist) \r
1163     {\r
1164         SendMessage(hlist, WM_SETREDRAW, TRUE, 0L);\r
1165         InvalidateRect(hlist, NULL, TRUE);\r
1166         UpdateWindow(hlist);\r
1167     }\r
1168     if (k_errno == EOF)\r
1169         k_errno = 0;\r
1170 \r
1171     /* XXX the if statement directly below was inserted to eliminate\r
1172        an error 20 on Leash startup. The error occurs from an error\r
1173        number thrown from krb_get_tf_realm.  We believe this change\r
1174        does not eliminate other errors, but it may. */\r
1175 \r
1176     if (k_errno == RET_NOTKT)\r
1177         k_errno = 0;\r
1178 \r
1179     ticketinfo->btickets = newtickets;\r
1180     if (k_errno != 0)\r
1181         return KRBERR(k_errno);\r
1182     return 0;\r
1183 }\r
1184 \r
1185 \r
1186 \r
1187 static BOOL CALLBACK \r
1188 EnumChildProc(HWND hwnd, LPARAM lParam)\r
1189 {\r
1190     HWND * h = (HWND *)lParam;\r
1191     *h = hwnd;\r
1192     return FALSE;\r
1193 }\r
1194 \r
1195 \r
1196 static HWND\r
1197 FindFirstChildWindow(HWND parent)\r
1198 {\r
1199     HWND hFirstChild = 0;\r
1200     EnumChildWindows(parent, EnumChildProc, (LPARAM) &hFirstChild);\r
1201         return hFirstChild;\r
1202 }\r
1203 \r
1204 void FAR\r
1205 not_an_API_Leash_AcquireInitialTicketsIfNeeded(krb5_context context, krb5_principal desiredKrb5Principal) \r
1206 {\r
1207     krb5_error_code     err;\r
1208     LSH_DLGINFO_EX      dlginfo;\r
1209     HGLOBAL hData;\r
1210     HWND    hLeash;\r
1211     HWND    hForeground;\r
1212     char                        *desiredName = 0;\r
1213     char                *desiredRealm = 0;\r
1214     char                *p;\r
1215     TicketList * list = NULL;\r
1216     TICKETINFO   ticketinfo;\r
1217     krb5_context        ctx;\r
1218     char newenv[256];\r
1219     char * env = 0;\r
1220     DWORD dwMsLsaImport = Leash_get_default_mslsa_import();\r
1221 \r
1222     char loginenv[16];\r
1223     BOOL prompt;\r
1224 \r
1225     GetEnvironmentVariable("KERBEROSLOGIN_NEVER_PROMPT", loginenv, sizeof(loginenv));\r
1226     prompt = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);\r
1227 \r
1228     if ( !prompt || !pkrb5_init_context )\r
1229         return;\r
1230 \r
1231     ctx = context;\r
1232     env = getenv("KRB5CCNAME");\r
1233     if ( !env && context ) {\r
1234         sprintf(newenv,"KRB5CCNAME=%s",pkrb5_cc_default_name(ctx));\r
1235         env = (char *)putenv(newenv);\r
1236     }\r
1237 \r
1238     not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx);\r
1239     not_an_API_LeashFreeTicketList(&list);\r
1240 \r
1241     if ( ticketinfo.btickets != GOOD_TICKETS && \r
1242          Leash_get_default_mslsa_import() && Leash_importable() ) {\r
1243         // We have the option of importing tickets from the MSLSA\r
1244         // but should we?  Do the tickets in the MSLSA cache belong \r
1245         // to the default realm used by Leash?  If so, import.  \r
1246         int import = 0;\r
1247 \r
1248         if ( dwMsLsaImport == 1 ) {             /* always import */\r
1249             import = 1;\r
1250         } else if ( dwMsLsaImport == 2 ) {      /* import when realms match */\r
1251             krb5_error_code code;\r
1252             krb5_ccache mslsa_ccache=0;\r
1253             krb5_principal princ = 0;\r
1254             char ms_realm[128] = "", *def_realm = 0, *r;\r
1255             int i;\r
1256 \r
1257             if (code = pkrb5_cc_resolve(ctx, "MSLSA:", &mslsa_ccache))\r
1258                 goto cleanup;\r
1259 \r
1260             if (code = pkrb5_cc_get_principal(ctx, mslsa_ccache, &princ))\r
1261                 goto cleanup;\r
1262 \r
1263             for ( r=ms_realm, i=0; i<krb5_princ_realm(ctx, princ)->length; r++, i++ ) {\r
1264                 *r = krb5_princ_realm(ctx, princ)->data[i];\r
1265             }\r
1266             *r = '\0';\r
1267 \r
1268             if (code = pkrb5_get_default_realm(ctx, &def_realm))\r
1269                 goto cleanup;\r
1270 \r
1271             import = !strcmp(def_realm, ms_realm);\r
1272 \r
1273           cleanup:\r
1274             if (def_realm)\r
1275                 pkrb5_free_default_realm(ctx, def_realm);\r
1276 \r
1277             if (princ)\r
1278                 pkrb5_free_principal(ctx, princ);\r
1279 \r
1280             if (mslsa_ccache)\r
1281                 pkrb5_cc_close(ctx, mslsa_ccache);\r
1282         }\r
1283 \r
1284         if ( import ) {\r
1285             Leash_import();\r
1286 \r
1287             not_an_API_LeashKRB5GetTickets(&ticketinfo,&list,&ctx);\r
1288             not_an_API_LeashFreeTicketList(&list);\r
1289         }\r
1290     }\r
1291 \r
1292     if ( ticketinfo.btickets != GOOD_TICKETS ) \r
1293     {\r
1294         /* do we want a specific client principal? */\r
1295         if (desiredKrb5Principal != NULL) {\r
1296             err = pkrb5_unparse_name (ctx, desiredKrb5Principal, &desiredName);\r
1297             if (!err) {\r
1298                 dlginfo.username = desiredName;\r
1299                 for (p = desiredName; *p && *p != '@'; p++);\r
1300                 if ( *p == '@' ) {\r
1301                     *p = '\0';\r
1302                     desiredRealm = dlginfo.realm = ++p;\r
1303                 }\r
1304             }\r
1305         }\r
1306                 \r
1307 #ifdef COMMENT\r
1308         memset(&dlginfo, 0, sizeof(LSH_DLGINFO_EX));\r
1309         dlginfo.size = sizeof(LSH_DLGINFO_EX);\r
1310         dlginfo.dlgtype = DLGTYPE_PASSWD;\r
1311         dlginfo.title = "Obtain Kerberos Ticket Getting Tickets";\r
1312         dlginfo.use_defaults = 1;\r
1313 \r
1314         err = Leash_kinit_dlg_ex(NULL, &dlginfo);\r
1315 #else\r
1316         /* construct a marshalling of data\r
1317          *   <title><principal><realm>\r
1318          * then send to Leash\r
1319          */\r
1320 \r
1321         hData = GlobalAlloc( GHND, 4096 );\r
1322         hForeground = GetForegroundWindow();\r
1323         hLeash = FindWindow("LEASH.0WNDCLASS", NULL);\r
1324         SetForegroundWindow(hLeash);\r
1325         hLeash = FindFirstChildWindow(hLeash);\r
1326         if ( hData && hLeash ) {\r
1327             char * strs = GlobalLock( hData );\r
1328             if ( strs ) {\r
1329                 strcpy(strs, "Obtain Kerberos Ticket Getting Tickets");\r
1330                 strs += strlen(strs) + 1;\r
1331                 if ( desiredName ) {\r
1332                     strcpy(strs, desiredName);\r
1333                     strs += strlen(strs) + 1;\r
1334                                         if (desiredRealm) {\r
1335                                                 strcpy(strs, desiredRealm);\r
1336                                                 strs += strlen(strs) + 1;\r
1337                                         }\r
1338                 } else {\r
1339                     *strs = 0;\r
1340                     strs++;\r
1341                     *strs = 0;\r
1342                     strs++;\r
1343                 }\r
1344 \r
1345                 GlobalUnlock( hData );\r
1346                 SendMessage(hLeash, 32809, 0, (LPARAM) hData);\r
1347             }\r
1348 \r
1349             GlobalFree( hData );\r
1350         }\r
1351         SetForegroundWindow(hForeground);\r
1352 #endif\r
1353         if (desiredName != NULL)\r
1354             pkrb5_free_unparsed_name(ctx, desiredName);\r
1355     }\r
1356 \r
1357     if ( !env && context )\r
1358         putenv("KRB5CCNAME=");\r
1359 \r
1360     if ( !context )\r
1361         pkrb5_free_context(ctx);\r
1362 }\r