1 /**
2 * Copyright (c) 2008 Moritz Bechler
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 **/
22 
23 /* CHANGELOG (more recent on top)
24  *
25  * 2010-05-27 Mark Seecof	add getPrincipal(), getRealm(), getLifetime(),
26  * 				renew(), getTktAttrs() methods; support more
27  * 				options to initKeytab/Password(); check only
28  * 				primary TGT in isValid(); fix some bugs.
29  *
30  * 2010-04-11 Moritz Bechler	RC2 release
31  */
32 
33 #include "config.h"
34 #include "php_krb5.h"
35 
36 #include "ext/standard/info.h"
37 #include "ext/standard/base64.h"
38 
39 #include <sys/time.h>
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 
43 #ifdef HAVE_KADM5
44 #include "kdb.h"
45 #endif
46 
47 zend_class_entry *krb5_ce_ccache;
48 
49 /* Class definition */
50 
51 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_none, 0, 0, 0)
52 ZEND_END_ARG_INFO()
53 
54 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_initPassword, 0, 0, 2)
55 	ZEND_ARG_INFO(0, principal)
56 	ZEND_ARG_INFO(0, pass)
57 	ZEND_ARG_ARRAY_INFO(0, options, 0)
58 ZEND_END_ARG_INFO()
59 
60 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_initKeytab, 0, 0, 2)
61 	ZEND_ARG_INFO(0, principal)
62 	ZEND_ARG_INFO(0, keytab)
63 	ZEND_ARG_ARRAY_INFO(0, options, 0)
64 ZEND_END_ARG_INFO()
65 
66 
67 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_changePassword, 0, 0, 3)
68 	ZEND_ARG_INFO(0, principal)
69 	ZEND_ARG_INFO(0, oldpass)
70 	ZEND_ARG_INFO(0, newpass)
71 ZEND_END_ARG_INFO()
72 
73 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_open, 0, 0, 1)
74 	ZEND_ARG_INFO(0, src)
75 ZEND_END_ARG_INFO()
76 
77 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_save, 0, 0, 1)
78 	ZEND_ARG_INFO(0, dest)
79 ZEND_END_ARG_INFO()
80 
81 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_isValid, 0, 0, 0)
82 	ZEND_ARG_INFO(0, timeRemain)
83 ZEND_END_ARG_INFO()
84 
85 ZEND_BEGIN_ARG_INFO_EX(arginfo_KRB5CCache_getTktAttrs, 0, 0, 0)
86 	ZEND_ARG_INFO(0, prefix)
87 ZEND_END_ARG_INFO()
88 
89 PHP_METHOD(KRB5CCache, initPassword);
90 PHP_METHOD(KRB5CCache, initKeytab);
91 PHP_METHOD(KRB5CCache, changePassword);
92 PHP_METHOD(KRB5CCache, getName);
93 PHP_METHOD(KRB5CCache, getPrincipal);
94 PHP_METHOD(KRB5CCache, getRealm);
95 PHP_METHOD(KRB5CCache, getLifetime);
96 PHP_METHOD(KRB5CCache, getEntries);
97 PHP_METHOD(KRB5CCache, open);
98 PHP_METHOD(KRB5CCache, save);
99 PHP_METHOD(KRB5CCache, isValid);
100 PHP_METHOD(KRB5CCache, getTktAttrs);
101 PHP_METHOD(KRB5CCache, renew);
102 PHP_METHOD(KRB5CCache, getExpirationTime);
103 
104 static zend_function_entry krb5_ccache_functions[] = {
105 		PHP_ME(KRB5CCache, initPassword, arginfo_KRB5CCache_initPassword, ZEND_ACC_PUBLIC)
106 		PHP_ME(KRB5CCache, initKeytab,   arginfo_KRB5CCache_initKeytab,   ZEND_ACC_PUBLIC)
107 		PHP_ME(KRB5CCache, changePassword,arginfo_KRB5CCache_changePassword,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
108 		PHP_ME(KRB5CCache, getName,      arginfo_KRB5CCache_none,         ZEND_ACC_PUBLIC)
109 		PHP_ME(KRB5CCache, getPrincipal, arginfo_KRB5CCache_none,         ZEND_ACC_PUBLIC)
110 		PHP_ME(KRB5CCache, getRealm,     arginfo_KRB5CCache_none,         ZEND_ACC_PUBLIC)
111 		PHP_ME(KRB5CCache, getLifetime,  arginfo_KRB5CCache_none,         ZEND_ACC_PUBLIC)
112 		PHP_ME(KRB5CCache, getEntries,   arginfo_KRB5CCache_none,         ZEND_ACC_PUBLIC)
113 		PHP_ME(KRB5CCache, open,         arginfo_KRB5CCache_open,         ZEND_ACC_PUBLIC)
114 		PHP_ME(KRB5CCache, save,         arginfo_KRB5CCache_save,         ZEND_ACC_PUBLIC)
115 		PHP_ME(KRB5CCache, isValid,      arginfo_KRB5CCache_isValid,      ZEND_ACC_PUBLIC)
116 		PHP_ME(KRB5CCache, getTktAttrs,  arginfo_KRB5CCache_getTktAttrs,  ZEND_ACC_PUBLIC)
117 		PHP_ME(KRB5CCache, renew,        arginfo_KRB5CCache_none,         ZEND_ACC_PUBLIC)
118 		PHP_ME(KRB5CCache, getExpirationTime,arginfo_KRB5CCache_none,     ZEND_ACC_PUBLIC)
119 		PHP_FE_END
120 };
121 
122 
123 zend_module_entry krb5_module_entry = {
124 #if ZEND_MODULE_API_NO >= 20010901
125     STANDARD_MODULE_HEADER,
126 #endif
127     PHP_KRB5_EXT_NAME,
128     NULL,
129     PHP_MINIT(krb5),
130     PHP_MSHUTDOWN(krb5),
131     NULL,
132     NULL,
133     PHP_MINFO(krb5),
134 #if ZEND_MODULE_API_NO >= 20010901
135     PHP_KRB5_VERSION,
136 #endif
137     STANDARD_MODULE_PROPERTIES
138 };
139 
140 #ifdef COMPILE_DL_KRB5
141 	ZEND_GET_MODULE(krb5)
142 #endif
143 
144 
145 krb5_error_code php_krb5_display_error(krb5_context ctx, krb5_error_code code, char* str TSRMLS_DC);
146 
147 /*  Initialization functions */
148 zend_object_handlers krb5_ccache_handlers;
149 
150 #if PHP_MAJOR_VERSION < 7
151 zend_object_value php_krb5_ticket_object_new( zend_class_entry *ce TSRMLS_DC);
152 #else
153 zend_object *php_krb5_ticket_object_new(zend_class_entry *ce TSRMLS_DC);
154 static void php_krb5_ccache_object_free(zend_object *obj TSRMLS_DC);
155 #endif
156 
PHP_MINIT_FUNCTION(krb5)157 PHP_MINIT_FUNCTION(krb5)
158 {
159 	zend_class_entry krb5_ccache;
160 
161 	INIT_CLASS_ENTRY(krb5_ccache, "KRB5CCache", krb5_ccache_functions);
162 	krb5_ce_ccache = zend_register_internal_class(&krb5_ccache TSRMLS_CC);
163 	krb5_ce_ccache->create_object = php_krb5_ticket_object_new;
164 	memcpy(&krb5_ccache_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
165 #if PHP_MAJOR_VERSION >= 7
166 	krb5_ccache_handlers.offset = XtOffsetOf(krb5_ccache_object, std);
167 	krb5_ccache_handlers.free_obj = php_krb5_ccache_object_free;
168 #endif
169 
170 #ifdef HAVE_KADM5
171 	if(php_krb5_kadm5_register_classes(TSRMLS_C) != SUCCESS) {
172 		return FAILURE;
173 	}
174 #endif
175 
176 	/* register constants */
177 	REGISTER_LONG_CONSTANT("GSS_C_DELEG_FLAG", GSS_C_DELEG_FLAG, CONST_CS | CONST_PERSISTENT );
178 	REGISTER_LONG_CONSTANT("GSS_C_MUTUAL_FLAG", GSS_C_MUTUAL_FLAG, CONST_CS | CONST_PERSISTENT );
179 	REGISTER_LONG_CONSTANT("GSS_C_REPLAY_FLAG", GSS_C_REPLAY_FLAG, CONST_CS | CONST_PERSISTENT );
180 	REGISTER_LONG_CONSTANT("GSS_C_SEQUENCE_FLAG", GSS_C_SEQUENCE_FLAG, CONST_CS | CONST_PERSISTENT );
181 	REGISTER_LONG_CONSTANT("GSS_C_CONF_FLAG", GSS_C_CONF_FLAG, CONST_CS | CONST_PERSISTENT );
182 	REGISTER_LONG_CONSTANT("GSS_C_INTEG_FLAG", GSS_C_INTEG_FLAG, CONST_CS | CONST_PERSISTENT );
183 	REGISTER_LONG_CONSTANT("GSS_C_ANON_FLAG", GSS_C_ANON_FLAG, CONST_CS | CONST_PERSISTENT );
184 	REGISTER_LONG_CONSTANT("GSS_C_PROT_READY_FLAG", GSS_C_PROT_READY_FLAG, CONST_CS | CONST_PERSISTENT );
185 	REGISTER_LONG_CONSTANT("GSS_C_TRANS_FLAG", GSS_C_TRANS_FLAG, CONST_CS | CONST_PERSISTENT );
186 
187 	REGISTER_LONG_CONSTANT("GSS_C_BOTH", GSS_C_BOTH, CONST_CS | CONST_PERSISTENT );
188 	REGISTER_LONG_CONSTANT("GSS_C_INITIATE", GSS_C_INITIATE, CONST_CS | CONST_PERSISTENT );
189 	REGISTER_LONG_CONSTANT("GSS_C_ACCEPT", GSS_C_ACCEPT, CONST_CS | CONST_PERSISTENT );
190 
191 	REGISTER_LONG_CONSTANT("GSS_C_NO_NAME", 0, CONST_CS | CONST_PERSISTENT );
192 
193 #ifdef KRB5_TL_DB_ARGS
194 	REGISTER_LONG_CONSTANT("KRB5_TL_DB_ARGS", KRB5_TL_DB_ARGS, CONST_CS | CONST_PERSISTENT );
195 #endif
196 
197 	if(php_krb5_gssapi_register_classes(TSRMLS_C) != SUCCESS) {
198 		return FAILURE;
199 	}
200 
201 	if(php_krb5_negotiate_auth_register_classes(TSRMLS_C) != SUCCESS) {
202 		return FAILURE;
203 	}
204 
205 	return SUCCESS;
206 }
207 
PHP_MSHUTDOWN_FUNCTION(krb5)208 PHP_MSHUTDOWN_FUNCTION(krb5)
209 {
210 	if(php_krb5_gssapi_shutdown(TSRMLS_C) != SUCCESS) {
211 		return FAILURE;
212 	}
213 
214 	return SUCCESS;
215 }
216 
217 
PHP_MINFO_FUNCTION(krb5)218 PHP_MINFO_FUNCTION(krb5)
219 {
220 	php_info_print_table_start();
221 	php_info_print_table_row(2, "Kerberos 5 support", "enabled");
222 	php_info_print_table_row(2, "Extension version", PHP_KRB5_VERSION);
223 #ifdef HAVE_KRB5_MIT
224 	php_info_print_table_row(2, "Kerberos library", "MIT");
225 #endif
226 
227 #ifdef HAVE_KRB5_HEIMDAL
228 	php_info_print_table_row(2, "Kerberos library", "Heimdal");
229 #endif
230 
231 #ifdef KRB5_VERSION
232 	php_info_print_table_row(2, "Library version", KRB5_VERSION);
233 #endif
234 
235 #ifdef HAVE_KADM5
236 	php_info_print_table_row(2, "KADM5 support", "yes");
237 #else
238 	php_info_print_table_row(2, "KADM5 support", "no");
239 #endif
240 
241 #if HAVE_GSS_KRB5_IMPORT_CRED
242 	php_info_print_table_row(2, "Import cred support", "yes");
243 #else
244 	php_info_print_table_row(2, "Import cred support", "no");
245 #endif
246 
247 	php_info_print_table_row(2, "GSSAPI/SPNEGO auth support", "yes");
248 	php_info_print_table_end();
249 }
250 
251 /*  Constructors/Destructors */
252 /* {{{ */
253 #if PHP_MAJOR_VERSION < 7
php_krb5_ccache_object_dtor(void * obj,zend_object_handle handle TSRMLS_DC)254 static void php_krb5_ccache_object_dtor(void *obj, zend_object_handle handle TSRMLS_DC)
255 {
256 	krb5_ccache_object *ticket = (krb5_ccache_object*)obj;
257 
258 
259 	if(ticket) {
260 		OBJECT_STD_DTOR(ticket->std);
261 
262 		krb5_cc_destroy(ticket->ctx, ticket->cc);
263 		krb5_free_context(ticket->ctx);
264 
265 		if(ticket->keytab) {
266 			efree(ticket->keytab);
267 		}
268 
269 		efree(ticket);
270 	}
271 }
272 #else
php_krb5_ccache_object_free(zend_object * obj TSRMLS_DC)273 static void php_krb5_ccache_object_free(zend_object *obj TSRMLS_DC)
274 {
275 	krb5_ccache_object *ticket = (krb5_ccache_object*)((char *)obj - XtOffsetOf(krb5_ccache_object, std));
276 	krb5_cc_destroy(ticket->ctx, ticket->cc);
277 	krb5_free_context(ticket->ctx);
278 
279 	if(ticket->keytab) {
280 		efree(ticket->keytab);
281 	}
282 	zend_object_std_dtor(obj);
283 }
284 #endif
285 /* }}} */
286 
287 
288 /* {{{ */
289 #if PHP_MAJOR_VERSION < 7
php_krb5_ticket_object_new(zend_class_entry * ce TSRMLS_DC)290 zend_object_value php_krb5_ticket_object_new(zend_class_entry *ce TSRMLS_DC)
291 {
292 	zend_object_value retval;
293 	zend_object *failed;
294 	krb5_ccache_object *object;
295 	krb5_error_code ret = 0;
296 
297 	object = emalloc(sizeof(krb5_ccache_object));
298 	memset(object, 0, sizeof(krb5_ccache_object));
299 
300 	/* intialize context */
301 	if((ret = krb5_init_context(&object->ctx))) {
302 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot initialize Kerberos5 context");
303 		efree(object);
304 		return zend_objects_new(&failed, ce TSRMLS_CC);
305 	}
306 
307 	// initialize random ccache
308 	if((ret = krb5_cc_new_unique(object->ctx, "MEMORY", "", &object->cc))) {
309 		const char *msg = krb5_get_error_message(object->ctx,ret);
310 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot open credential cache: %s", msg);
311 		krb5_free_error_message(object->ctx, msg);
312 		krb5_free_context(object->ctx);
313 		efree(object);
314 		return zend_objects_new(&failed, ce TSRMLS_CC);
315 	}
316 
317 
318 	INIT_STD_OBJECT(object->std, ce);
319 #if PHP_VERSION_ID < 50399
320 	zend_hash_copy(object->std.properties, &ce->default_properties,
321 	        		(copy_ctor_func_t) zval_add_ref, NULL,
322 					sizeof(zval*));
323 #else
324 	object_properties_init(&(object->std), ce);
325 #endif
326 
327 	retval.handle = zend_objects_store_put(object, php_krb5_ccache_object_dtor, NULL, NULL TSRMLS_CC);
328 	retval.handlers = &krb5_ccache_handlers;
329 	return retval;
330 }
331 #else
php_krb5_ticket_object_new(zend_class_entry * ce TSRMLS_DC)332 zend_object *php_krb5_ticket_object_new(zend_class_entry *ce TSRMLS_DC)
333 {
334 	krb5_ccache_object *object;
335 	krb5_error_code ret = 0;
336 
337 	object = ecalloc(1, sizeof(krb5_ccache_object) + zend_object_properties_size(ce));
338 
339 	/* intialize context */
340 	if((ret = krb5_init_context(&object->ctx))) {
341 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot initialize Kerberos5 context");
342 		efree(object);
343 		return zend_objects_new(ce);
344 	}
345 
346 	// initialize random ccache
347 	if((ret = krb5_cc_new_unique(object->ctx, "MEMORY", "", &object->cc))) {
348 		const char *msg = krb5_get_error_message(object->ctx,ret);
349 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot open credential cache: %s", msg);
350 		krb5_free_error_message(object->ctx, msg);
351 		krb5_free_context(object->ctx);
352 		efree(object);
353 		return zend_objects_new(ce);
354 	}
355 
356 	zend_object_std_init(&object->std, ce TSRMLS_CC);
357 	object_properties_init(&object->std, ce);
358 	object->std.handlers = &krb5_ccache_handlers;
359 	return &object->std;
360 }
361 #endif
362 /* }}} */
363 
364 /* Helper functions */
365 /* {{{ Parse options array for initKeytab()/initPassword() */
php_krb5_parse_init_creds_opts(zval * opts,krb5_get_init_creds_opt * cred_opts,char ** in_tkt_svc,char ** vfy_keytab TSRMLS_DC)366 static int php_krb5_parse_init_creds_opts(zval *opts, krb5_get_init_creds_opt *cred_opts, char **in_tkt_svc, char **vfy_keytab TSRMLS_DC)
367 {
368 	int retval = 0;
369 	zval *tmp = NULL;
370 	zend_string *str = NULL;
371 
372 	if (Z_TYPE_P(opts) != IS_ARRAY) {
373 		return KRB5KRB_ERR_GENERIC;
374 	}
375 
376 	/* forwardable */
377 	tmp = zend_compat_hash_find(HASH_OF(opts), "forwardable", sizeof("forwardable"));
378 	if (tmp != NULL) {
379 		krb5_get_init_creds_opt_set_forwardable(cred_opts, zval_is_true(tmp));
380 	}
381 
382 
383 	/* proxiable */
384 	tmp = zend_compat_hash_find(HASH_OF(opts), "proxiable", sizeof("proxiable"));
385 	if (tmp != NULL) {
386 		krb5_get_init_creds_opt_set_proxiable(cred_opts, zval_is_true(tmp));
387 	}
388 
389 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
390 	/* canonicalize */
391 	tmp = zend_compat_hash_find(HASH_OF(opts), "canonicalize", sizeof("canonicalize"));
392 	if (tmp != NULL) {
393 		krb5_get_init_creds_opt_set_canonicalize(cred_opts, zval_is_true(tmp));
394 	}
395 #endif /* KRB5_GET_INIT_CREDS_OPT_CANONICALIZE */
396 
397 	/* tkt_life */
398 	tmp = zend_compat_hash_find(HASH_OF(opts), "tkt_life", sizeof("tkt_life"));
399 	if (tmp != NULL) {
400 		krb5_get_init_creds_opt_set_tkt_life(cred_opts, zval_get_long(tmp TSRMLS_CC));
401 	}
402 
403 	/* renew_life */
404 	tmp = zend_compat_hash_find(HASH_OF(opts), "renew_life", sizeof("renew_life"));
405 	if (tmp != NULL) {
406 		krb5_get_init_creds_opt_set_renew_life(cred_opts, zval_get_long(tmp TSRMLS_CC));
407 	}
408 
409 	/* service_name (krb5 arg "in_tkt_service") */
410 	tmp = zend_compat_hash_find(HASH_OF(opts), "service_name", sizeof("service_name"));
411 	if (tmp != NULL) {
412 		str = zval_get_string(tmp TSRMLS_CC);
413 		if ((*in_tkt_svc = emalloc(1+str->len))) {
414 			strncpy(*in_tkt_svc, str->val, str->len);
415 			(*in_tkt_svc)[str->len] = '\0';
416 		}
417 
418 		zend_string_release(str);
419 	}
420 
421 	/* verification keytab name */
422 	tmp = zend_compat_hash_find(HASH_OF(opts), "verify_keytab", sizeof("verify_keytab"));
423 	if (tmp != NULL) {
424 		str = zval_get_string(tmp TSRMLS_CC);
425 		if ((*vfy_keytab = emalloc(1+str->len))) {
426 			strncpy(*vfy_keytab, str->val, str->len);
427 			(*vfy_keytab)[str->len] = '\0';
428 		}
429 
430 		zend_string_release(str);
431 	}
432 
433 	return retval;
434 } /* }}} */
435 
436 /* {{{ */
php_krb5_display_error(krb5_context ctx,krb5_error_code code,char * str TSRMLS_DC)437 krb5_error_code php_krb5_display_error(krb5_context ctx, krb5_error_code code, char* str TSRMLS_DC) {
438 	const char *errstr = krb5_get_error_message(ctx,code);
439 	zend_throw_exception_ex(NULL, 0 TSRMLS_CC, str, errstr);
440 	krb5_free_error_message(ctx, errstr);
441 	return code;
442 }
443 /* }}} */
444 
445 /* {{{  Copies one ccache to another*/
php_krb5_copy_ccache(krb5_context ctx,const krb5_ccache src,krb5_ccache dest TSRMLS_DC)446 static krb5_error_code php_krb5_copy_ccache(krb5_context ctx, const krb5_ccache src, krb5_ccache dest TSRMLS_DC)
447 {
448 	krb5_error_code retval = 0;
449 	krb5_principal princ;
450 
451 	if((retval = krb5_cc_get_principal(ctx,src,&princ))) {
452 		return php_krb5_display_error(ctx, retval,  "Failed to retrieve principal from source ccache (%s)" TSRMLS_CC);
453 	}
454 
455 	if((retval = krb5_cc_initialize(ctx,dest,princ))) {
456 		krb5_free_principal(ctx, princ);
457 		return php_krb5_display_error(ctx, retval,  "Failed to initialize destination ccache (%s)" TSRMLS_CC);
458 	}
459 
460 	krb5_free_principal(ctx, princ);
461 
462 
463 #ifdef HAVE_KRB5_HEIMDAL
464 	if((retval = krb5_cc_copy_cache(ctx, src, dest))) {
465 		return php_krb5_display_error(ctx, retval,  "Cannot copy given credential cache (%s)" TSRMLS_CC);
466 	}
467 #else
468 	krb5_cc_cursor cursor;
469 	if((retval = krb5_cc_start_seq_get(ctx,src,&cursor))) {
470 		return retval;
471 	}
472 
473 	krb5_creds creds;
474 	while(krb5_cc_next_cred(ctx,src,&cursor,&creds) == 0) {
475 		if((retval = krb5_cc_store_cred(ctx, dest,&creds))) {
476 			krb5_cc_end_seq_get(ctx,src,&cursor);
477 			return retval;
478 		}
479 		krb5_free_cred_contents(ctx, &creds);
480 	}
481 
482 	krb5_cc_end_seq_get(ctx,src,&cursor);
483 #endif
484 
485 	return retval;
486 }
487 /* }}} */
488 
489 
490 /* {{{ extract realm string */
php_krb5_get_realm(krb5_context ctx,krb5_principal princ TSRMLS_DC)491 static char *php_krb5_get_realm(krb5_context ctx, krb5_principal princ TSRMLS_DC)
492 {
493 #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM
494 	return krb5_principal_get_realm(ctx, princ);
495 #else
496 	krb5_data *data;
497 
498 	data = krb5_princ_realm(ctx, princ);
499 	if ((data != NULL) && (data->data != NULL)) return data->data;
500 	/* else */
501 	return NULL;
502 #endif
503 }
504 /* }}} */
505 
506 /* {{{ Get expiration times for primary TGT in cache */
php_krb5_get_tgt_expire(krb5_ccache_object * ccache,long * endtime,long * renew_until TSRMLS_DC)507 static krb5_error_code php_krb5_get_tgt_expire(krb5_ccache_object *ccache, long *endtime, long *renew_until TSRMLS_DC)
508 {
509 	krb5_error_code retval = 0;
510 	char *errstr = NULL;
511 	krb5_principal princ;
512 	int have_princ = 0;
513 	krb5_creds in_cred;
514 	int have_in_cred = 0;
515 	krb5_creds *credptr = NULL;
516 	int have_credptr = 0;
517 	char *realm;
518 
519 	do {
520 		memset(&princ, 0, sizeof(princ));
521 		if ((retval = krb5_cc_get_principal(ccache->ctx,ccache->cc, &princ))) {
522 			errstr = "Failed to retrieve principal from source ccache (%s)";
523 			break;
524 		}
525 		have_princ = 1;
526 
527 		if (!(realm = php_krb5_get_realm(ccache->ctx, princ TSRMLS_CC))) {
528 			retval = KRB5KRB_ERR_GENERIC;
529 			errstr = "Failed to extract realm from principal (%s)";
530 			break;
531 		}
532 
533 		memset(&in_cred, 0, sizeof(in_cred));
534 		in_cred.client = princ;
535 
536 		if ((retval = krb5_build_principal(ccache->ctx, &in_cred.server, strlen(realm), realm, "krbtgt", realm, NULL))) {
537 			errstr = "Failed to build krbtgt principal (%s)";
538 			break;
539 		}
540 		have_in_cred = 1;
541 
542 		if ((retval = krb5_get_credentials(ccache->ctx, KRB5_GC_CACHED, ccache->cc, &in_cred, &credptr))) {
543 			errstr = "Failed to retrieve krbtgt ticket from cache (%s)";
544 			break;
545 		}
546 		have_credptr = 1;
547 
548 	} while (0);
549 
550 	if (have_princ) krb5_free_principal(ccache->ctx, princ);
551 	if (have_in_cred) krb5_free_principal(ccache->ctx, in_cred.server);
552 
553 	if (have_credptr) {
554 		krb5_free_cred_contents(ccache->ctx, credptr);
555 		*endtime = credptr->times.endtime;
556 		*renew_until = credptr->times.renew_till;
557 		free(credptr);
558 	}
559 
560 	if (errstr != NULL) {
561 		php_krb5_display_error(ccache->ctx, retval, errstr TSRMLS_CC);
562 	}
563 
564 	return retval;
565 }
566 /* }}} */
567 
568 /* {{{ verify a (client's) new TGT using keytab */
php_krb5_verify_tgt(krb5_ccache_object * ccache,krb5_creds * creds,char * vfy_keytab TSRMLS_DC)569 static krb5_error_code php_krb5_verify_tgt(krb5_ccache_object *ccache, krb5_creds *creds, char *vfy_keytab TSRMLS_DC)
570 {
571 	krb5_error_code retval = 0;
572 	krb5_error_code r2val;
573 	krb5_keytab ktab;
574 	int have_ktab = 0;
575 	krb5_kt_cursor cursor;
576 	int have_cursor = 0;
577 	krb5_keytab_entry entry;
578 	int have_entry = 0;
579 	krb5_principal princ;
580 	int have_princ = 0;
581 	krb5_verify_init_creds_opt opts;
582 
583 
584 	if (!vfy_keytab || !*vfy_keytab) {
585 		return KRB5_KT_NOTFOUND;
586 	}
587 
588     do {
589 	memset(&ktab, 0, sizeof(ktab));
590 	if ((retval = krb5_kt_resolve(ccache->ctx, vfy_keytab, &ktab))) {
591 		break;
592 	}
593 	have_ktab = 1;
594 
595 	memset(&cursor, 0, sizeof(cursor));
596 	if ((retval = krb5_kt_start_seq_get(ccache->ctx, ktab, &cursor))) {
597 		break;
598 	}
599 	have_cursor = 1;
600 
601 	memset(&entry, 0, sizeof(entry));
602 	if ((retval = krb5_kt_next_entry(ccache->ctx, ktab, &entry, &cursor))) {
603 		break;
604 	}
605 	have_entry = 1;
606 
607 	memset(&princ, 0, sizeof(princ));
608 	if ((retval = krb5_copy_principal(ccache->ctx, entry.principal, &princ))) {
609 		break;
610 	}
611 	have_princ = 1;
612 
613 	krb5_verify_init_creds_opt_init(&opts);
614 	krb5_verify_init_creds_opt_set_ap_req_nofail(&opts, 1);
615 
616 	if ((retval = krb5_verify_init_creds(ccache->ctx, creds, princ, ktab, NULL, &opts))) {
617 		break;
618 	}
619     } while (0);
620 
621         if (have_ktab && (r2val = krb5_kt_close(ccache->ctx, ktab))) {
622 		php_krb5_display_error(ccache->ctx, r2val, "Failed to close keytab (%s)" TSRMLS_CC);
623 	}
624 
625 	if (have_cursor && (r2val = krb5_kt_end_seq_get(ccache->ctx, ktab, &cursor))) {
626 		php_krb5_display_error(ccache->ctx, r2val, "Failed to free keytab cursor (%s)" TSRMLS_CC);
627 	}
628 
629 	if (have_entry &&
630 #ifdef HAVE_KRB5_HEIMDAL
631 		(r2val = krb5_kt_free_entry(ccache->ctx, &entry))
632 #else
633 		(r2val = krb5_free_keytab_entry_contents(ccache->ctx, &entry))
634 #endif
635 			) {
636 		php_krb5_display_error(ccache->ctx, r2val, "Failed to free keytab entry (%s)" TSRMLS_CC);
637 	}
638 
639 	if (have_princ) krb5_free_principal(ccache->ctx, princ);
640 
641 	return retval;
642 }
643 /* }}} */
644 
645 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_EXPIRE_CALLBACK
646 /* {{{ received and store password and account expiration times */
expire_callback_func(krb5_context context,void * data,krb5_timestamp password_expiration,krb5_timestamp account_expiration,krb5_boolean is_last_req)647 void expire_callback_func(krb5_context context, void *data, krb5_timestamp password_expiration, krb5_timestamp account_expiration, krb5_boolean is_last_req) {
648     krb5_ccache_object *ccache = (krb5_ccache_object *) data;
649     ccache->exp_received = TRUE;
650     ccache->exp_password = password_expiration;
651     ccache->exp_account = account_expiration;
652     ccache->exp_is_last_req = is_last_req;
653 }
654 #endif
655 /* }}} */
656 
657 /* KRB5CCache Methods */
658 
659 /* {{{ proto string KRB5CCache::getName(  )
660    Gets the name/identifier of this credential cache */
PHP_METHOD(KRB5CCache,getName)661 PHP_METHOD(KRB5CCache, getName)
662 {
663 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
664 	const char *tmpname = krb5_cc_get_name(ccache->ctx, ccache->cc);
665 	const char *tmptype = krb5_cc_get_type(ccache->ctx, ccache->cc);
666 	char *name = NULL;
667 
668 	if (zend_parse_parameters_none() == FAILURE) {
669 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
670 		RETURN_FALSE;
671 	}
672 
673 	name = emalloc(strlen(tmpname) + strlen(tmptype) + 2);
674 	*name = 0;
675 	strcat(name, tmptype);
676 	strcat(name, ":");
677 	strcat(name, tmpname);
678 	_RETVAL_STRING(name);
679 	efree(name);
680 }
681 /* }}} */
682 
683 /* {{{ proto bool KRB5CCache::open( string $src )
684    Copies the contents of the credential cache given by $dest to this credential cache */
PHP_METHOD(KRB5CCache,open)685 PHP_METHOD(KRB5CCache, open)
686 {
687 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
688 	char *sccname = NULL;
689 	strsize_t sccname_len = 0;
690 	krb5_error_code retval = 0;
691 
692 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, ARG_PATH, &sccname, &sccname_len) == FAILURE) {
693 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
694 		RETURN_FALSE;
695 	}
696 
697 	krb5_ccache src;
698 
699 	if((retval = krb5_cc_resolve(ccache->ctx, sccname, &src))) {
700 		php_krb5_display_error(ccache->ctx, retval,  "Cannot open given credential cache (%s)" TSRMLS_CC);
701 		RETURN_FALSE;
702 	}
703 
704 	if((retval = php_krb5_copy_ccache(ccache->ctx, src, ccache->cc TSRMLS_CC))) {
705 		krb5_cc_close(ccache->ctx, src);
706 		php_krb5_display_error(ccache->ctx, retval,  "Failed to copy credential cache (%s)" TSRMLS_CC);
707 		RETURN_FALSE;
708 	}
709 
710 	krb5_cc_close(ccache->ctx, src);
711 	RETURN_TRUE;
712 }
713 /* }}} */
714 
715 /* {{{ proto bool KRB5CCache::save( string $dest )
716    Copies the contents of this credential cache to the credential cache given by $dest */
PHP_METHOD(KRB5CCache,save)717 PHP_METHOD(KRB5CCache, save)
718 {
719 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
720 	char *sccname = NULL;
721 	strsize_t sccname_len = 0;
722 	krb5_error_code retval = 0;
723 
724 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, ARG_PATH, &sccname, &sccname_len) == FAILURE) {
725 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
726 		RETURN_FALSE;
727 	}
728 
729 	krb5_ccache dest = NULL;
730 	if((retval = krb5_cc_resolve(ccache->ctx, sccname, &dest))) {
731 		php_krb5_display_error(ccache->ctx, retval,  "Cannot open given credential cache (%s)" TSRMLS_CC);
732 		RETURN_FALSE;
733 	}
734 
735 	if((retval = php_krb5_copy_ccache(ccache->ctx, ccache->cc, dest TSRMLS_CC))) {
736 		krb5_cc_close(ccache->ctx, dest);
737 		php_krb5_display_error(ccache->ctx, retval,  "Failed to copy credential cache (%s)" TSRMLS_CC);
738 		RETURN_FALSE;
739 	}
740 
741 	krb5_cc_close(ccache->ctx, dest);
742 	RETURN_TRUE;
743 }
744 /* }}} */
745 
746 /* {{{ proto bool KRB5CCache::initPassword( string $principal, string $pass [, array $options ])
747    Gets a TGT for the given principal using the given credentials */
PHP_METHOD(KRB5CCache,initPassword)748 PHP_METHOD(KRB5CCache, initPassword)
749 {
750 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
751 	krb5_error_code retval = 0;
752 	char *errstr = "";
753 
754 	char *sprinc = NULL;
755 	strsize_t sprinc_len = 0;
756 	char *spass = NULL;
757 	strsize_t spass_len = 0;
758 	zval *opts = NULL;
759 
760 	krb5_principal princ;
761 	int have_princ = 0;
762 	krb5_get_init_creds_opt *cred_opts;
763 	int have_cred_opts = 0;
764 	char *in_tkt_svc = NULL;
765 	char *vfy_keytab = NULL;
766 	krb5_creds creds;
767 	int have_creds = 0;
768 
769 #ifndef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
770 	krb5_get_init_creds_opt cred_opts_struct;
771 	cred_opts = &cred_opts_struct;
772 #endif
773 
774 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a", &sprinc, &sprinc_len, &spass, &spass_len, &opts) == FAILURE) {
775 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
776 		RETURN_FALSE;
777 	}
778 
779     do {
780 	memset(&princ, 0, sizeof(princ));
781 	if ((retval = krb5_parse_name(ccache->ctx, sprinc, &princ))) {
782 		errstr = "Cannot parse Kerberos principal (%s)";
783 		break;
784 	}
785 	have_princ = 1;
786 
787 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
788 	if ((retval = krb5_get_init_creds_opt_alloc(ccache->ctx, &cred_opts))) {
789 		errstr = "Cannot allocate cred_opts (%s)";
790 		break;
791 	}
792 #else
793 	krb5_get_init_creds_opt_init(cred_opts);
794 #endif
795 	have_cred_opts = 1;
796 
797 	if (opts != NULL) {
798 		if ((retval = php_krb5_parse_init_creds_opts(opts, cred_opts, &in_tkt_svc, &vfy_keytab TSRMLS_CC))) {
799 			errstr = "Cannot parse credential options (%s)";
800 			break;
801 		}
802 	}
803 
804 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_EXPIRE_CALLBACK
805 	krb5_get_init_creds_opt_set_expire_callback(ccache->ctx,
806 			cred_opts,
807 			expire_callback_func,
808 			ccache);
809 #endif
810 
811 	memset(&creds, 0, sizeof(creds));
812 	if ((retval = krb5_get_init_creds_password(ccache->ctx, &creds, princ, spass, NULL, 0, 0, in_tkt_svc, cred_opts))) {
813 		errstr = "Cannot get ticket (%s)";
814 		break;
815 	}
816 	have_creds = 1;
817 
818 	if((retval = krb5_cc_initialize(ccache->ctx, ccache->cc, princ))) {
819 		errstr = "Failed to initialize credential cache (%s)";
820 		break;
821 	}
822 
823 	if((retval = krb5_cc_store_cred(ccache->ctx, ccache->cc, &creds))) {
824 		errstr = "Failed to store ticket in credential cache (%s)";
825 		break;
826 	}
827 
828 	if (vfy_keytab && *vfy_keytab && (retval = php_krb5_verify_tgt(ccache, &creds, vfy_keytab TSRMLS_CC))) {
829 		errstr = "Failed to verify ticket (%s)";
830 		break;
831 	}
832 
833     } while (0);
834 
835 	if (have_princ) krb5_free_principal(ccache->ctx, princ);
836 
837 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
838 	if (have_cred_opts) krb5_get_init_creds_opt_free(ccache->ctx, cred_opts);
839 #endif
840 
841 	if (in_tkt_svc) efree(in_tkt_svc);
842 	if (vfy_keytab) efree(vfy_keytab);
843 	if (have_creds) krb5_free_cred_contents(ccache->ctx, &creds);
844 
845 	if (retval) {
846 		php_krb5_display_error(ccache->ctx, retval, errstr TSRMLS_CC);
847 		RETURN_FALSE;
848 	}
849 
850 	RETURN_TRUE;
851 }
852 /* }}} */
853 
854 /* {{{ proto bool KRB5CCache::initKeytab( string $principal, string $keytab_file [, array $options ])
855    Gets a TGT for the given principal using the credentials in the given keytab */
PHP_METHOD(KRB5CCache,initKeytab)856 PHP_METHOD(KRB5CCache, initKeytab)
857 {
858 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
859 	krb5_error_code retval = 0;
860 	char *errstr = "";
861 
862 	char *sprinc = NULL;
863 	strsize_t sprinc_len = 0;
864 	char *skeytab = NULL;
865 	strsize_t skeytab_len = 0;
866 	zval *opts = NULL;
867 
868 	krb5_principal princ;
869 	int have_princ = 0;
870 	krb5_keytab keytab;
871 	int have_keytab = 0;
872 	krb5_get_init_creds_opt *cred_opts;
873 	int have_cred_opts = 0;
874 	char *in_tkt_svc = NULL;
875 	char *vfy_keytab = NULL;
876 	krb5_creds creds;
877 	int have_creds = 0;
878 
879 #ifndef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
880 	krb5_get_init_creds_opt cred_opts_struct;
881 	cred_opts = &cred_opts_struct;
882 #endif
883 
884 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s" ARG_PATH "|a", &sprinc, &sprinc_len, &skeytab, &skeytab_len, &opts) == FAILURE) {
885 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
886 		RETURN_FALSE;
887 	}
888 
889 #if PHP_VERSION_ID < 50399
890 	if ( (PG(safe_mode) &&
891 			!php_checkuid(skeytab, NULL, CHECKUID_CHECK_FILE_AND_DIR)) ||
892 		php_check_open_basedir(skeytab TSRMLS_CC)) {
893 		RETURN_FALSE;
894 	}
895 #else
896 	if ( php_check_open_basedir(skeytab TSRMLS_CC)) {
897 		RETURN_FALSE;
898 	}
899 #endif
900 
901     do {
902 	memset(&princ, 0, sizeof(princ));
903 	if ((retval = krb5_parse_name(ccache->ctx, sprinc, &princ))) {
904 		errstr = "Cannot parse Kerberos principal (%s)";
905 		break;
906 	}
907 	have_princ = 1;
908 
909 	memset(&keytab, 0, sizeof(keytab));
910 	if ((retval = krb5_kt_resolve(ccache->ctx, skeytab, &keytab))) {
911 		errstr = "Cannot load keytab (%s)";
912 		break;
913 	}
914 	have_keytab = 1;
915 
916 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
917 	if ((retval = krb5_get_init_creds_opt_alloc(ccache->ctx, &cred_opts))) {
918 		errstr = "Cannot allocate cred_opts (%s)";
919 		break;
920 	}
921 #else
922 	krb5_get_init_creds_opt_init(cred_opts);
923 #endif
924 	have_cred_opts = 1;
925 
926 	if(opts) {
927 		if ((retval = php_krb5_parse_init_creds_opts(opts, cred_opts, &in_tkt_svc, &vfy_keytab TSRMLS_CC))) {
928 			errstr = "Cannot parse credential options";
929 			break;
930 		}
931 	}
932 
933 	memset(&creds, 0, sizeof(creds));
934 	if ((retval = krb5_get_init_creds_keytab(ccache->ctx, &creds, princ, keytab, 0, in_tkt_svc, cred_opts))) {
935 		errstr = "Cannot get ticket (%s)";
936 		break;
937 	}
938 	have_creds = 1;
939 
940 	if ((retval = krb5_cc_initialize(ccache->ctx, ccache->cc, princ))) {
941 		errstr = "Failed to initialize credential cache (%s)";
942 		break;
943 	}
944 
945 	if((retval = krb5_cc_store_cred(ccache->ctx, ccache->cc, &creds))) {
946 		errstr = "Failed to store ticket in credential cache (%s)";
947 		break;
948 	}
949 
950 	if (vfy_keytab && *vfy_keytab && (retval = php_krb5_verify_tgt(ccache, &creds, vfy_keytab TSRMLS_CC))) {
951 		errstr = "Failed to verify ticket (%s)";
952 		break;
953 	}
954 
955     } while (0);
956 
957 	if (have_princ) krb5_free_principal(ccache->ctx, princ);
958 	if (have_keytab) krb5_kt_close(ccache->ctx, keytab);
959 
960 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
961 	if (have_cred_opts) krb5_get_init_creds_opt_free(ccache->ctx, cred_opts);
962 #endif
963 
964 	if (in_tkt_svc) efree(in_tkt_svc);
965 	if (vfy_keytab) efree(vfy_keytab);
966 	if (have_creds) krb5_free_cred_contents(ccache->ctx, &creds);
967 
968 	if (retval) {
969 		php_krb5_display_error(ccache->ctx, retval, errstr TSRMLS_CC);
970 		RETURN_FALSE;
971 	}
972 
973 	ccache->keytab = estrdup(skeytab);
974 	RETURN_TRUE;
975 }
976 /* }}} */
977 
978 /* {{{ proto string KRB5CCache::getPrincipal( )
979    Returns name of primary principal (client) associated with cache */
PHP_METHOD(KRB5CCache,getPrincipal)980 PHP_METHOD(KRB5CCache, getPrincipal)
981 {
982 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
983 	krb5_error_code retval = 0;
984 	krb5_principal princ;
985 	char *princname = NULL;
986 
987 	if (zend_parse_parameters_none() == FAILURE) {
988 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
989 		RETURN_FALSE;
990 	}
991 
992 	memset(&princ, 0, sizeof(princ));
993 	if ((retval = krb5_cc_get_principal(ccache->ctx, ccache->cc, &princ))) {
994 		php_krb5_display_error(ccache->ctx, retval, "Failed to retrieve principal from source ccache (%s)" TSRMLS_CC);
995 		RETURN_EMPTY_STRING();
996 	}
997 
998 	if ((retval = krb5_unparse_name(ccache->ctx, princ, &princname))) {
999 		krb5_free_principal(ccache->ctx,princ);
1000 		php_krb5_display_error(ccache->ctx, retval, "Failed to unparse principal name (%s)" TSRMLS_CC);
1001 		RETURN_EMPTY_STRING();
1002 	}
1003 
1004 	_RETVAL_STRING(princname);
1005 	krb5_free_unparsed_name(ccache->ctx,princname);
1006 	krb5_free_principal(ccache->ctx,princ);
1007 }
1008 /* }}} */
1009 
1010 /* {{{ proto string KRB5CCache::getRealm( )
1011    Returns name of realm for primary principal */
PHP_METHOD(KRB5CCache,getRealm)1012 PHP_METHOD(KRB5CCache, getRealm)
1013 {
1014 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
1015 	krb5_error_code retval = 0;
1016 	krb5_principal princ;
1017 	char *realm;
1018 
1019 	if (zend_parse_parameters_none() == FAILURE) {
1020 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
1021 		RETURN_FALSE;
1022 	}
1023 
1024 	memset(&princ, 0, sizeof(princ));
1025 	if ((retval = krb5_cc_get_principal(ccache->ctx,ccache->cc,&princ))) {
1026 		php_krb5_display_error(ccache->ctx, retval, "Failed to retrieve principal from source ccache (%s)" TSRMLS_CC);
1027 		RETURN_EMPTY_STRING();
1028 	}
1029 
1030 	if (!(realm = php_krb5_get_realm(ccache->ctx, princ TSRMLS_CC))) {
1031 		krb5_free_principal(ccache->ctx,princ);
1032 		php_krb5_display_error(ccache->ctx, KRB5KRB_ERR_GENERIC, "Failed to extract realm from principal (%s)" TSRMLS_CC);
1033 		RETURN_EMPTY_STRING();
1034 	}
1035 
1036 	_RETVAL_STRING(realm);
1037 	krb5_free_principal(ccache->ctx,princ);
1038 }
1039 /* }}} */
1040 
1041 /* {{{ proto array KRB5CCache::getLifetime( )
1042    Return array with primary TGT's endtime and renew_until times in it */
PHP_METHOD(KRB5CCache,getLifetime)1043 PHP_METHOD(KRB5CCache, getLifetime)
1044 {
1045 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
1046 	krb5_error_code retval = 0;
1047 	long endtime, renew_until;
1048 
1049 	if (zend_parse_parameters_none() == FAILURE) {
1050 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
1051 		RETURN_FALSE;
1052 	}
1053 
1054 	array_init(return_value);
1055 
1056 	if ((retval = php_krb5_get_tgt_expire(ccache,&endtime,&renew_until TSRMLS_CC))) {
1057 		php_krb5_display_error(ccache->ctx, retval, "Failed to get TGT times (%s)" TSRMLS_CC);
1058 		return;
1059 	}
1060 
1061 	add_assoc_long(return_value, "endtime", endtime);
1062 	add_assoc_long(return_value, "renew_until", renew_until);
1063 }
1064 /* }}} */
1065 
1066 /* {{{ proto array KRB5CCache::getEntries( )
1067    Fetches all principal names for which tickets are available in this cache */
PHP_METHOD(KRB5CCache,getEntries)1068 PHP_METHOD(KRB5CCache, getEntries)
1069 {
1070 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
1071 	krb5_error_code retval = 0;
1072 	char *errstr = "";
1073 	krb5_cc_cursor cursor;
1074 	int have_cursor = 0;
1075 	krb5_creds creds;
1076 	int have_creds = 0;
1077 	char *princname;
1078 
1079 	if (zend_parse_parameters_none() == FAILURE) {
1080 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
1081 		RETURN_FALSE;
1082 	}
1083 
1084 	array_init(return_value);
1085 
1086     do {
1087 	memset(&cursor, 0, sizeof(cursor));
1088 	if((retval = krb5_cc_start_seq_get(ccache->ctx,ccache->cc,&cursor))) {
1089 		errstr = "Failed to initialize ccache iterator (%s)";
1090 		break;
1091 	}
1092 	have_cursor = 1;
1093 
1094 	memset(&creds, 0, sizeof(creds));
1095 	while(krb5_cc_next_cred(ccache->ctx,ccache->cc,&cursor,&creds) == 0) {
1096 		have_creds = 1;
1097 		if(creds.server) {
1098 			princname = NULL;
1099 			if((retval = krb5_unparse_name(ccache->ctx, creds.server, &princname))) {
1100 				errstr = "Failed to unparse principal name (%s)";
1101 				break;
1102 			}
1103 			_add_next_index_string(return_value, princname);
1104 			krb5_free_unparsed_name(ccache->ctx, princname);
1105 		}
1106 		krb5_free_cred_contents(ccache->ctx, &creds);
1107 		have_creds = 0;
1108 	}
1109 
1110     } while (0);
1111 
1112 	if (have_creds) krb5_free_cred_contents(ccache->ctx, &creds);
1113 
1114 	if (have_cursor) krb5_cc_end_seq_get(ccache->ctx, ccache->cc, &cursor);
1115 
1116 	if (*errstr) {
1117 		php_krb5_display_error(ccache->ctx, retval, errstr TSRMLS_CC);
1118 		array_init(return_value);
1119 	}
1120 }
1121 /* }}} */
1122 
1123 /* {{{ proto bool KRB5CCache::isValid( [ int $timeRemain = 0 ] )
1124    Checks whether the primary TGT in the cache is still valid and will remain valid for the given number of seconds */
PHP_METHOD(KRB5CCache,isValid)1125 PHP_METHOD(KRB5CCache, isValid)
1126 {
1127 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
1128 	krb5_error_code retval = 0;
1129 	long endtime, renew_until, then;
1130 	krb5_timestamp now;
1131 	zend_long need = 0;
1132 
1133 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &need) == FAILURE) {
1134 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
1135 		RETURN_FALSE;
1136 	}
1137 
1138 	if ((retval = php_krb5_get_tgt_expire(ccache,&endtime,&renew_until TSRMLS_CC))) {
1139 		RETURN_FALSE;
1140 	}
1141 
1142 	if((retval = krb5_timeofday(ccache->ctx, &now))) {
1143 		php_krb5_display_error(ccache->ctx, retval, "Failed to obtain time (%s)" TSRMLS_CC);
1144 	}
1145 
1146 	then = now + need + 60; /* modest allowance for clock drift */
1147 
1148 	if (then > endtime) {
1149 			RETURN_FALSE;
1150 		}
1151 
1152 	RETURN_TRUE;
1153 }
1154 /* }}} */
1155 
1156 /* {{{ proto array KRB5CCache::getTktAttrs( [string prefix])
1157    Fetches principals and (readable) attributes of ticket(s) in cache */
PHP_METHOD(KRB5CCache,getTktAttrs)1158 PHP_METHOD(KRB5CCache, getTktAttrs)
1159 {
1160 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
1161 	krb5_error_code retval = 0;
1162 	char *errstr = "";
1163 	krb5_cc_cursor cursor;
1164 	int have_cursor = 0;
1165 	krb5_creds creds;
1166 	int have_creds = 0;
1167 	_DECLARE_ZVAL(tktinfo);
1168 	char *princname;
1169 	long princ_len;
1170 	long tktflags;
1171 	char strflags[65];
1172 	char *q = strflags + sizeof(strflags) - 1;
1173 	char *p;
1174 	krb5_ticket *tkt;
1175 	char *encstr;
1176 #define ENCSTRMAX 256
1177 	krb5_address *tktaddr, **tkt_addrs;
1178 	_DECLARE_ZVAL(addrlist);
1179 	struct in_addr ipaddr;
1180 #ifdef INET6_ADDRSTRLEN
1181 	struct in6_addr ip6addr;
1182 	char straddr[INET6_ADDRSTRLEN];
1183 #endif
1184 	char *prefix = NULL;
1185 	strsize_t pfx_len = 0;
1186 
1187 	array_init(return_value);
1188 
1189 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &prefix, &pfx_len) == FAILURE) {
1190 		return;
1191 	}
1192 	if (pfx_len == 0) prefix = NULL;
1193 
1194     do {
1195 	memset(&cursor, 0, sizeof(cursor));
1196 	if ((retval = krb5_cc_start_seq_get(ccache->ctx,ccache->cc,&cursor))) {
1197 		errstr = "Failed to initialize ccache iterator (%s)";
1198 		break;
1199 	}
1200 	have_cursor = 1;
1201 
1202 	memset(&creds, 0, sizeof(creds));
1203 	while (krb5_cc_next_cred(ccache->ctx,ccache->cc,&cursor,&creds) == 0) {
1204 		have_creds = 1;
1205 		if (creds.server) {
1206 			_ALLOC_INIT_ZVAL(tktinfo);
1207 			array_init(tktinfo);
1208 
1209 			princname = NULL;
1210 			if ((retval = krb5_unparse_name(ccache->ctx, creds.server, &princname))) {
1211 				errstr = "Failed to unparse server principal name (%s)";
1212 				break;
1213 			}
1214 
1215 			princ_len = princname ? strlen(princname) : 0;
1216 			if (prefix && ((princ_len < pfx_len) || strncmp(princname,prefix,pfx_len))) {
1217 				free(princname);
1218 				krb5_free_cred_contents(ccache->ctx, &creds);
1219 				have_creds = 0;
1220 				continue;
1221 			}
1222 
1223 			_add_assoc_string(tktinfo, "server", (princname?princname:""));
1224 
1225 			krb5_free_unparsed_name(ccache->ctx, princname);
1226 
1227 			princname = NULL;
1228 			if((retval = krb5_unparse_name(ccache->ctx, creds.client, &princname))) {
1229 				errstr = "Failed to unparse client principal name (%s)";
1230 				break;
1231 			}
1232 			_add_assoc_string(tktinfo, "client", (princname?princname:""));
1233 			krb5_free_unparsed_name(ccache->ctx, princname);
1234 
1235 			add_assoc_long(tktinfo, "authtime", creds.times.authtime);
1236 			add_assoc_long(tktinfo, "starttime", creds.times.starttime);
1237 			add_assoc_long(tktinfo, "endtime", creds.times.endtime);
1238 
1239 			/* Darn it, "till" is NOT an abbreviation of "until" */
1240 			add_assoc_long(tktinfo, "renew_until", creds.times.renew_till);
1241 
1242 			tktflags = creds.ticket_flags;
1243 			p = strflags;
1244 			*p = '\0';
1245 			if ((tktflags & TKT_FLG_FORWARDABLE) && (p < q)) *(p++) = 'F';
1246 			if ((tktflags & TKT_FLG_FORWARDED) && (p < q)) *(p++) = 'f';
1247 			if ((tktflags & TKT_FLG_PROXIABLE) && (p < q)) *(p++) = 'P';
1248 			if ((tktflags & TKT_FLG_PROXY) && (p < q)) *(p++) = 'p';
1249 			if ((tktflags & TKT_FLG_MAY_POSTDATE) && (p < q)) *(p++) = 'D';
1250 			if ((tktflags & TKT_FLG_POSTDATED) && (p < q)) *(p++) = 'd';
1251 			if ((tktflags & TKT_FLG_INVALID) && (p < q)) *(p++) = 'i';
1252 			if ((tktflags & TKT_FLG_RENEWABLE) && (p < q)) *(p++) = 'R';
1253 			if ((tktflags & TKT_FLG_INITIAL) && (p < q)) *(p++) = 'I';
1254 			if ((tktflags & TKT_FLG_PRE_AUTH) && (p < q)) *(p++) = 'A';
1255 			if ((tktflags & TKT_FLG_HW_AUTH) && (p < q)) *(p++) = 'H';
1256 			if ((tktflags & TKT_FLG_TRANSIT_POLICY_CHECKED) && (p < q)) *(p++) = 'T';
1257 			if ((tktflags & TKT_FLG_OK_AS_DELEGATE) && (p < q)) *(p++) = 'O';
1258 #ifdef TKT_FLG_ENC_PA_REP
1259 			if ((tktflags & TKT_FLG_ENC_PA_REP) && (p < q)) *(p++) = 'e';
1260 #endif
1261 			if ((tktflags & TKT_FLG_ANONYMOUS) && (p < q)) *(p++) = 'a';
1262 			*p = '\0';
1263 
1264 			_add_assoc_string(tktinfo, "flags", strflags);
1265 
1266 #ifdef HAVE_KRB5_HEIMDAL
1267 			encstr = NULL;
1268 			if ((retval = krb5_enctype_to_string(ccache->ctx,creds.keyblock.enctype, &encstr)))
1269 #else
1270 			encstr = malloc(ENCSTRMAX);
1271 			if ((retval = krb5_enctype_to_string(creds.keyblock.enctype, encstr, ENCSTRMAX)))
1272 #endif
1273 			{
1274 				if (!encstr) encstr = malloc(ENCSTRMAX);
1275 				snprintf(encstr, ENCSTRMAX, "enctype %d", creds.keyblock.enctype);
1276 			}
1277 			_add_assoc_string(tktinfo, "skey_enc", encstr);
1278 			free(encstr);
1279 
1280 			if ((retval = krb5_decode_ticket(&creds.ticket,&tkt))) {
1281 				errstr = "Failed to decode ticket data (%s)";
1282 				break;
1283 			} else {
1284 #ifdef HAVE_KRB5_HEIMDAL
1285 				encstr = NULL;
1286 				if((retval = krb5_enctype_to_string(ccache->ctx,creds.keyblock.enctype, &encstr)))
1287 #else
1288 				encstr = malloc(ENCSTRMAX);
1289 				if((retval = krb5_enctype_to_string(tkt->enc_part.enctype, encstr, ENCSTRMAX)))
1290 #endif
1291 				{
1292 					if (!encstr) encstr = malloc(ENCSTRMAX);
1293 					snprintf(encstr, ENCSTRMAX, "enctype %d", tkt->enc_part.enctype);
1294 				}
1295 				_add_assoc_string(tktinfo, "tkt_enc", encstr);
1296 				free(encstr);
1297 				krb5_free_ticket(ccache->ctx, tkt);
1298 			}
1299 
1300 			_ALLOC_INIT_ZVAL(addrlist);
1301 			array_init(addrlist);
1302 			tkt_addrs = creds.addresses;
1303 			if (tkt_addrs) while((tktaddr = *(tkt_addrs++))) {
1304 				if ((tktaddr->addrtype == ADDRTYPE_INET) && (tktaddr->length == 4)) {
1305 					memcpy(&(ipaddr.s_addr), tktaddr->contents, tktaddr->length);
1306 
1307 #ifndef INET6_ADDRSTRLEN
1308 					_add_next_index_string(addrlist, inet_ntoa(ipaddr));
1309 				}
1310 #if 0
1311  { match curlies
1312 #endif
1313 #else /* ! INET6_ADDRSTRLEN */
1314 					if (inet_ntop(AF_INET, &ipaddr, straddr, sizeof(straddr))) {
1315 						_add_next_index_string(addrlist, straddr);
1316 					}
1317 				}
1318 				if ((tktaddr->addrtype == ADDRTYPE_INET6) && (tktaddr->length >= 4)) {
1319 					memcpy(ip6addr.s6_addr, tktaddr->contents, tktaddr->length);
1320 					if (inet_ntop(AF_INET6, &ipaddr, straddr, sizeof(straddr))) {
1321 						_add_next_index_string(addrlist, straddr);
1322 					}
1323 				}
1324 #endif /* INET6_ADDRSTRLEN */
1325 			}
1326 			add_assoc_zval(tktinfo, "addresses", addrlist);
1327 			add_next_index_zval(return_value,tktinfo);
1328 		}
1329 
1330 		krb5_free_cred_contents(ccache->ctx, &creds);
1331 		have_creds = 0;
1332 	} /* while creds */
1333 
1334 	if (have_creds) krb5_free_cred_contents(ccache->ctx, &creds);
1335 
1336     } while (0);
1337 
1338 	if (have_cursor) krb5_cc_end_seq_get(ccache->ctx,ccache->cc,&cursor);
1339 
1340 	if (*errstr) {
1341 		php_krb5_display_error(ccache->ctx, retval, errstr TSRMLS_CC);
1342 		array_init(return_value);
1343 	}
1344 }
1345 #undef ENCSTRMAX
1346 /* }}} */
1347 
1348 /* {{{ proto bool KRB5CCache::renew( )
1349    Renew default TGT and purge other tickets from cache, return FALSE on failure  */
PHP_METHOD(KRB5CCache,renew)1350 PHP_METHOD(KRB5CCache, renew)
1351 {
1352 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
1353 	krb5_error_code retval = 0;
1354 	char *errstr = "";
1355 	long endtime, renew_until;
1356 	krb5_timestamp now;
1357 	krb5_principal princ;
1358 	int have_princ = 0;
1359 	krb5_creds creds;
1360 	int have_creds = 0;
1361 
1362 	if (zend_parse_parameters_none() == FAILURE) {
1363 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
1364 		RETURN_FALSE;
1365 	}
1366 
1367     do {
1368 	if ((retval = php_krb5_get_tgt_expire(ccache, &endtime, &renew_until TSRMLS_CC))) {
1369 		errstr = "Failed to get renew_until () (%s)";
1370 		break;
1371 	}
1372 
1373 	if ((retval = krb5_timeofday(ccache->ctx, &now))) {
1374 		errstr = "Failed to read clock in renew() (%s)";
1375 		break;
1376 	}
1377 
1378 	if (now > renew_until) {
1379 		/* ticket is not renewable, but... */
1380 		if (now >= endtime) retval = -1; /* ...is it still useful? */
1381 		break;
1382 	}
1383 
1384 	memset(&princ, 0, sizeof(princ));
1385 	if ((retval = krb5_cc_get_principal(ccache->ctx, ccache->cc, &princ))) {
1386 		errstr = "Failed to get principal from cache (%s)";
1387 		break;
1388 	}
1389 	have_princ = 1;
1390 
1391 	memset(&creds, 0, sizeof(creds));
1392 	if ((retval = krb5_get_renewed_creds(ccache->ctx, &creds, princ, ccache->cc, NULL))) {
1393 		errstr = "Failed to renew TGT in cache (%s)";
1394 		break;
1395 	}
1396 	have_creds = 1;
1397 
1398 	if ((retval = krb5_cc_initialize(ccache->ctx, ccache->cc, princ))) {
1399 		errstr = "Failed to reinitialize ccache after TGT renewal (%s)";
1400 		break;
1401 	}
1402 
1403 	if((retval = krb5_cc_store_cred(ccache->ctx, ccache->cc, &creds))) {
1404 		errstr = "Failed to store renewed TGT in ccache (%s)";
1405 		break;
1406 	}
1407     } while (0);
1408 
1409 	if (have_princ) krb5_free_principal(ccache->ctx, princ);
1410 	if (have_creds) krb5_free_cred_contents(ccache->ctx, &creds);
1411 
1412 	if (retval) {
1413 		if (*errstr) {
1414 			php_krb5_display_error(ccache->ctx, retval, errstr TSRMLS_CC);
1415 		}
1416 		RETURN_FALSE;
1417 	}
1418 
1419 	/* otherwise */
1420 	RETURN_TRUE;
1421 }
1422 /* }}} */
1423 
1424 
1425 /* {{{ proto bool KRB5CCache::changePassword( string $principal, string $oldpass, string $newpass )
1426    Changes a principal's password using kpasswd */
PHP_METHOD(KRB5CCache,changePassword)1427 PHP_METHOD(KRB5CCache, changePassword)
1428 {
1429 
1430 	krb5_error_code retval = 0;
1431 	krb5_context ctx = NULL;
1432 	char *errstr = "";
1433 	char *message = NULL;
1434 
1435 	char *sprinc = NULL;
1436 	strsize_t sprinc_len = 0;
1437 	char *opass = NULL;
1438 	strsize_t opass_len = 0;
1439 	char *npass = NULL;
1440 	strsize_t npass_len = 0;
1441 
1442 	krb5_principal princ;
1443 	int have_princ = 0;
1444 	krb5_get_init_creds_opt *cred_opts;
1445 	int have_cred_opts = 0;
1446 	krb5_creds creds;
1447 	int have_creds = 0;
1448 	int result_code;
1449         krb5_data result_code_string, result_string;
1450 
1451 
1452 #ifndef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
1453 	krb5_get_init_creds_opt cred_opts_struct;
1454 	cred_opts = &cred_opts_struct;
1455 #endif
1456 
1457 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &sprinc, &sprinc_len, &opass, &opass_len, &npass, &npass_len) == FAILURE) {
1458 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
1459 		RETURN_FALSE;
1460 	}
1461 
1462     do {
1463  	retval = krb5_init_context(&ctx);
1464 	if ( retval ) {
1465 		errstr = "Failed to initialize context (%s)";
1466 		break;
1467 	}
1468 
1469 	memset(&princ, 0, sizeof(princ));
1470 	if ((retval = krb5_parse_name(ctx, sprinc, &princ))) {
1471 		errstr = "Cannot parse Kerberos principal (%s)";
1472 		break;
1473 	}
1474 	have_princ = 1;
1475 
1476 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
1477 	if ((retval = krb5_get_init_creds_opt_alloc(ctx, &cred_opts))) {
1478 		errstr = "Cannot allocate cred_opts (%s)";
1479 		break;
1480 	}
1481 #else
1482 	krb5_get_init_creds_opt_init(cred_opts);
1483 #endif
1484 	have_cred_opts = 1;
1485 
1486 	krb5_get_init_creds_opt_set_tkt_life(cred_opts, 5*60);
1487 	krb5_get_init_creds_opt_set_renew_life(cred_opts, 0);
1488 	krb5_get_init_creds_opt_set_forwardable(cred_opts, 0);
1489 	krb5_get_init_creds_opt_set_proxiable(cred_opts, 0);
1490 
1491 	memset(&creds, 0, sizeof(creds));
1492 	if ((retval = krb5_get_init_creds_password(ctx, &creds, princ, opass, NULL, 0, 0, "kadmin/changepw", cred_opts))) {
1493 		errstr = "Cannot get ticket (%s)";
1494 		break;
1495 	}
1496 	have_creds = 1;
1497 
1498 	if ((retval = krb5_change_password(ctx, &creds, npass,
1499 					&result_code, &result_code_string,
1500 					&result_string))) {
1501 		errstr = "Failed to change password (%s)";
1502 		break;
1503 	}
1504 
1505 	if (result_code != KRB5_KPASSWD_SUCCESS) {
1506 #ifdef HAVE_KRB5_CHPW_MESSAGE
1507 		if (krb5_chpw_message(ctx, &result_string, &message) != 0)
1508 			message = NULL;
1509 #endif
1510 		krb5_free_principal(ctx, princ);
1511 		krb5_free_cred_contents(ctx, &creds);
1512 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
1513 		if (have_cred_opts) krb5_get_init_creds_opt_free(ctx, cred_opts);
1514 #endif
1515 
1516 		zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "%.*s: %s",  (int) result_code_string.length,
1517 						result_code_string.data,
1518 		                                message ? message : result_string.data);
1519 #ifdef HAVE_KRB5_FREE_STRING
1520 		krb5_free_string(ctx, message);
1521 #else
1522 		free(message);
1523 #endif
1524 		RETURN_FALSE;
1525 	}
1526 
1527     } while (0);
1528 
1529 	if (have_princ) krb5_free_principal(ctx, princ);
1530 	if (have_creds) krb5_free_cred_contents(ctx, &creds);
1531 #ifdef KRB5_GET_INIT_CREDS_OPT_CANONICALIZE
1532 	if (have_cred_opts) krb5_get_init_creds_opt_free(ctx, cred_opts);
1533 #endif
1534 
1535 	if (retval) {
1536 		if ( ctx == NULL ) {
1537 			zend_throw_exception_ex(NULL, 0 TSRMLS_CC, errstr, retval);
1538 		} else if (*errstr) {
1539 			php_krb5_display_error(ctx, retval, errstr TSRMLS_CC);
1540 		}
1541 		RETURN_FALSE;
1542 	}
1543 
1544 	/* otherwise */
1545 	RETURN_TRUE;
1546 }
1547 /* }}} */
1548 
1549 /* {{{ proto array KRB5CCache::getExpirationTime( )
1550    Return array with password and account expiry times */
PHP_METHOD(KRB5CCache,getExpirationTime)1551 PHP_METHOD(KRB5CCache, getExpirationTime)
1552 {
1553 	krb5_ccache_object *ccache = KRB5_THIS_CCACHE;
1554 
1555 	if (zend_parse_parameters_none() == FAILURE) {
1556 		zend_throw_exception(NULL, "Failed to parse arglist", 0 TSRMLS_CC);
1557 		RETURN_FALSE;
1558 	}
1559 
1560 	array_init(return_value);
1561 	add_assoc_bool(return_value, "received", ccache->exp_received);
1562 	add_assoc_long(return_value, "password_expiration", ccache->exp_password);
1563 	add_assoc_long(return_value, "account_expiration", ccache->exp_account);
1564 	add_assoc_bool(return_value, "is_last_req", ccache->exp_is_last_req);
1565 }
1566 /* }}} */
1567 
1568 /* bottom of file */
1569