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