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