1 /**
2  * xrdp: A Remote Desktop Protocol server.
3  *
4  * Copyright (C) Jay Sorg 2004-2013
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 /**
20  *
21  * @file verify_user_kerberos.c
22  * @brief Authenticate user using kerberos
23  * @author Jay Sorg
24  *
25  */
26 
27 #if defined(HAVE_CONFIG_H)
28 #include <config_ac.h>
29 #endif
30 
31 #include "arch.h"
32 #include "os_calls.h"
33 #include "string_calls.h"
34 
35 #include <krb5.h>
36 
37 typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type;
38 
39 struct k_opts
40 {
41     /* in seconds */
42     krb5_deltat starttime;
43     krb5_deltat lifetime;
44     krb5_deltat rlife;
45 
46     int forwardable;
47     int proxiable;
48     int addresses;
49 
50     int not_forwardable;
51     int not_proxiable;
52     int no_addresses;
53 
54     int verbose;
55 
56     char *principal_name;
57     char *service_name;
58     char *keytab_name;
59     char *k5_cache_name;
60     char *k4_cache_name;
61 
62     action_type action;
63 };
64 
65 struct k5_data
66 {
67     krb5_context ctx;
68     krb5_ccache cc;
69     krb5_principal me;
70     char *name;
71 };
72 
73 struct user_info
74 {
75     const char *name;
76     const char *pass;
77 };
78 
79 /******************************************************************************/
80 /* returns boolean */
81 static int
k5_begin(struct k_opts * opts,struct k5_data * k5,struct user_info * u_info)82 k5_begin(struct k_opts *opts, struct k5_data *k5, struct user_info *u_info)
83 {
84     krb5_error_code code = 0;
85 
86     code = krb5_init_context(&k5->ctx);
87 
88     if (code != 0)
89     {
90         g_printf("krb5_init_context failed in k5_begin\n");
91         return 0;
92     }
93 
94     if (opts->k5_cache_name)
95     {
96         code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc);
97 
98         if (code != 0)
99         {
100             g_printf("krb5_cc_resolve failed in k5_begin\n");
101             return 0;
102         }
103     }
104     else
105     {
106         code = krb5_cc_default(k5->ctx, &k5->cc);
107 
108         if (code != 0)
109         {
110             g_printf("krb5_cc_default failed in k5_begin\n");
111             return 0;
112         }
113     }
114 
115     if (opts->principal_name)
116     {
117         /* Use specified name */
118         code = krb5_parse_name(k5->ctx, opts->principal_name, &k5->me);
119 
120         if (code != 0)
121         {
122             g_printf("krb5_parse_name failed in k5_begin\n");
123             return 0;
124         }
125     }
126     else
127     {
128         /* No principal name specified */
129         if (opts->action == INIT_KT)
130         {
131             /* Use the default host/service name */
132             code = krb5_sname_to_principal(k5->ctx, NULL, NULL,
133                                            KRB5_NT_SRV_HST, &k5->me);
134 
135             if (code != 0)
136             {
137                 g_printf("krb5_sname_to_principal failed in k5_begin\n");
138                 return 0;
139             }
140         }
141         else
142         {
143             /* Get default principal from cache if one exists */
144             code = krb5_cc_get_principal(k5->ctx, k5->cc, &k5->me);
145 
146             if (code != 0)
147             {
148                 code = krb5_parse_name(k5->ctx, u_info->name, &k5->me);
149 
150                 if (code != 0)
151                 {
152                     g_printf("krb5_parse_name failed in k5_begin\n");
153                     return 0;
154                 }
155             }
156         }
157     }
158 
159     code = krb5_unparse_name(k5->ctx, k5->me, &k5->name);
160 
161     if (code != 0)
162     {
163         g_printf("krb5_unparse_name failed in k5_begin\n");
164         return 0;
165     }
166 
167     opts->principal_name = k5->name;
168     return 1;
169 }
170 
171 /******************************************************************************/
172 static void
k5_end(struct k5_data * k5)173 k5_end(struct k5_data *k5)
174 {
175     if (k5->name)
176     {
177         krb5_free_unparsed_name(k5->ctx, k5->name);
178     }
179 
180     if (k5->me)
181     {
182         krb5_free_principal(k5->ctx, k5->me);
183     }
184 
185     if (k5->cc)
186     {
187         krb5_cc_close(k5->ctx, k5->cc);
188     }
189 
190     if (k5->ctx)
191     {
192         krb5_free_context(k5->ctx);
193     }
194 
195     g_memset(k5, 0, sizeof(struct k5_data));
196 }
197 
198 /******************************************************************************/
199 static krb5_error_code KRB5_CALLCONV
kinit_prompter(krb5_context ctx,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])200 kinit_prompter(krb5_context ctx, void *data, const char *name,
201                const char *banner, int num_prompts, krb5_prompt prompts[])
202 {
203     int i;
204     krb5_prompt_type *types;
205     krb5_error_code rc;
206     struct user_info *u_info;
207 
208     u_info = (struct user_info *)data;
209     rc = 0;
210     types = krb5_get_prompt_types(ctx);
211 
212     for (i = 0; i < num_prompts; i++)
213     {
214         if (types[i] == KRB5_PROMPT_TYPE_PASSWORD ||
215                 types[i] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN)
216         {
217             g_strncpy(prompts[i].reply->data, u_info->pass, 255);
218         }
219     }
220 
221     return rc;
222 }
223 
224 /******************************************************************************/
225 /* returns boolean */
226 static int
k5_kinit(struct k_opts * opts,struct k5_data * k5,struct user_info * u_info)227 k5_kinit(struct k_opts *opts, struct k5_data *k5, struct user_info *u_info)
228 {
229     const char *doing;
230     int notix = 1;
231     krb5_keytab keytab = 0;
232     krb5_creds my_creds;
233     krb5_error_code code = 0;
234     krb5_get_init_creds_opt options;
235     krb5_address **addresses;
236 
237     krb5_get_init_creds_opt_init(&options);
238     g_memset(&my_creds, 0, sizeof(my_creds));
239 
240     /*
241       From this point on, we can goto cleanup because my_creds is
242       initialized.
243     */
244     if (opts->lifetime)
245     {
246         krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime);
247     }
248 
249     if (opts->rlife)
250     {
251         krb5_get_init_creds_opt_set_renew_life(&options, opts->rlife);
252     }
253 
254     if (opts->forwardable)
255     {
256         krb5_get_init_creds_opt_set_forwardable(&options, 1);
257     }
258 
259     if (opts->not_forwardable)
260     {
261         krb5_get_init_creds_opt_set_forwardable(&options, 0);
262     }
263 
264     if (opts->proxiable)
265     {
266         krb5_get_init_creds_opt_set_proxiable(&options, 1);
267     }
268 
269     if (opts->not_proxiable)
270     {
271         krb5_get_init_creds_opt_set_proxiable(&options, 0);
272     }
273 
274     if (opts->addresses)
275     {
276         addresses = NULL;
277         code = krb5_os_localaddr(k5->ctx, &addresses);
278 
279         if (code != 0)
280         {
281             g_printf("krb5_os_localaddr failed in k5_kinit\n");
282             goto cleanup;
283         }
284 
285         krb5_get_init_creds_opt_set_address_list(&options, addresses);
286     }
287 
288     if (opts->no_addresses)
289     {
290         krb5_get_init_creds_opt_set_address_list(&options, NULL);
291     }
292 
293     if ((opts->action == INIT_KT) && opts->keytab_name)
294     {
295         code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab);
296 
297         if (code != 0)
298         {
299             g_printf("krb5_kt_resolve failed in k5_kinit\n");
300             goto cleanup;
301         }
302     }
303 
304     switch (opts->action)
305     {
306         case INIT_PW:
307             code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
308                                                 0, kinit_prompter, u_info,
309                                                 opts->starttime,
310                                                 opts->service_name,
311                                                 &options);
312             break;
313         case INIT_KT:
314             code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
315                                               keytab,
316                                               opts->starttime,
317                                               opts->service_name,
318                                               &options);
319             break;
320         case VALIDATE:
321             code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc,
322                                             opts->service_name);
323             break;
324         case RENEW:
325             code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->cc,
326                                           opts->service_name);
327             break;
328     }
329 
330     if (code != 0)
331     {
332         doing = 0;
333 
334         switch (opts->action)
335         {
336             case INIT_PW:
337             case INIT_KT:
338                 doing = "getting initial credentials";
339                 break;
340             case VALIDATE:
341                 doing = "validating credentials";
342                 break;
343             case RENEW:
344                 doing = "renewing credentials";
345                 break;
346         }
347 
348         if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
349         {
350             g_printf("sesman: Password incorrect while %s in k5_kinit\n", doing);
351         }
352         else
353         {
354             g_printf("sesman: error while %s in k5_kinit\n", doing);
355         }
356 
357         goto cleanup;
358     }
359 
360     if (!opts->lifetime)
361     {
362         /* We need to figure out what lifetime to use for Kerberos 4. */
363         opts->lifetime = my_creds.times.endtime - my_creds.times.authtime;
364     }
365 
366     code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me);
367 
368     if (code != 0)
369     {
370         g_printf("krb5_cc_initialize failed in k5_kinit\n");
371         goto cleanup;
372     }
373 
374     code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds);
375 
376     if (code != 0)
377     {
378         g_printf("krb5_cc_store_cred failed in k5_kinit\n");
379         goto cleanup;
380     }
381 
382     notix = 0;
383 
384 cleanup:
385 
386     if (my_creds.client == k5->me)
387     {
388         my_creds.client = 0;
389     }
390 
391     krb5_free_cred_contents(k5->ctx, &my_creds);
392 
393     if (keytab)
394     {
395         krb5_kt_close(k5->ctx, keytab);
396     }
397 
398     return notix ? 0 : 1;
399 }
400 
401 /******************************************************************************/
402 /* returns boolean */
403 int
auth_userpass(const char * user,const char * pass,int * errorcode)404 auth_userpass(const char *user, const char *pass, int *errorcode)
405 {
406     struct k_opts opts;
407     struct k5_data k5;
408     struct user_info u_info;
409     int got_k5;
410     int authed_k5;
411 
412     g_memset(&opts, 0, sizeof(opts));
413     opts.action = INIT_PW;
414     g_memset(&k5, 0, sizeof(k5));
415     g_memset(&u_info, 0, sizeof(u_info));
416     u_info.name = user;
417     u_info.pass = pass;
418     authed_k5 = 0;
419     got_k5 = k5_begin(&opts, &k5, &u_info);
420 
421     if (got_k5)
422     {
423         authed_k5 = k5_kinit(&opts, &k5, &u_info);
424         k5_end(&k5);
425     }
426 
427     return authed_k5;
428 }
429 
430 /******************************************************************************/
431 /* returns error */
432 int
auth_start_session(long in_val,int in_display)433 auth_start_session(long in_val, int in_display)
434 {
435     return 0;
436 }
437 
438 /******************************************************************************/
439 /* returns error */
440 int
auth_stop_session(long in_val)441 auth_stop_session(long in_val)
442 {
443     return 0;
444 }
445 
446 /******************************************************************************/
447 int
auth_end(long in_val)448 auth_end(long in_val)
449 {
450     return 0;
451 }
452 
453 /******************************************************************************/
454 int
auth_set_env(long in_val)455 auth_set_env(long in_val)
456 {
457     return 0;
458 }
459