Krb5.xs   [plain text]


#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <krb5.h>
#include <com_err.h>
#include "krb5_constants.c"

#ifdef __cplusplus
}
#endif

/* change this if 10 hours doesn't suit you */
#define KRB5_DEFAULT_LIFE 60*60*10

typedef krb5_ccache		Authen__Krb5__Ccache;
typedef krb5_principal		Authen__Krb5__Principal;
typedef krb5_auth_context	Authen__Krb5__AuthContext;
typedef krb5_rcache		Authen__Krb5__Rcache;
typedef krb5_creds		*Authen__Krb5__Creds;
typedef krb5_ap_rep_enc_part	*Authen__Krb5__ApRepEncPart;
typedef krb5_ticket		*Authen__Krb5__Ticket;
typedef krb5_keytab		Authen__Krb5__Keytab;
typedef krb5_enc_tkt_part	*Authen__Krb5__EncTktPart;
typedef krb5_error		*Authen__Krb5__Error;
typedef krb5_address		*Authen__Krb5__Address;
typedef krb5_keyblock		*Authen__Krb5__Keyblock;
typedef krb5_keytab_entry	*Authen__Krb5__KeytabEntry;
typedef krb5_kt_cursor          *Authen__Krb5__KeytabCursor;
typedef krb5_cc_cursor          *Authen__Krb5__CcacheCursor;
typedef krb5_keyblock		*Authen__Krb5__KeyBlock;

static krb5_context context = 0;
static krb5_error_code err;
static krb5_keytab_entry keytab_entry_init;

/*
 * The following three routines implement a "safehouse" for nested Kerberos
 * data structures which shouldn't be freed before their parent data
 * structures are freed.  Without this, "Bad free() ignored" errors as well
 * as core dumps could occur when the parent structures are eventually freed.
 *
 * If a method returns a newly allocated object, it calls can_free() to
 * register the object as "freeable," since the memory was not in use
 * beforehand.  This module will only free objects that have been registered
 * with can_free(), and lets Kerberos free the others.
 *
 * Doing it the other way (registering objects which *shouldn't* be freed)
 * is more complicated than it first seems, so I did it this way.
 */


HV *free_hash = NULL; /* might as well take advantage of Perl! */

void can_free(SV *sv)
{
	char key[80];

	sprintf(key,"%p",sv);
	if (!free_hash) free_hash = newHV();
	hv_store(free_hash,key,strlen(key),&PL_sv_yes,0);
}

int should_free(SV *sv)
{
	char key[80];

	if (!free_hash) return 0;
	sprintf(key,"%p",sv);
	return hv_exists(free_hash,key,strlen(key));
}

void freed(SV *sv)
{
	char key[80];

	if (!free_hash) return;
	sprintf(key,"%p",sv);
	hv_delete(free_hash,key,strlen(key),G_DISCARD);
}


MODULE = Authen::Krb5		PACKAGE = Authen::Krb5		PREFIX = krb5_

double
constant(name, arg)
	char *name
	int arg

void
krb5_error(e = 0)
	krb5_error_code e;

	CODE:
	if (e) {
		ST(0) = sv_2mortal(newSVpv((char *)error_message(e), 0));
	}
	else {
		ST(0) = sv_2mortal(newSVpv((char *)error_message(err), 0));
		SvUPGRADE(ST(0), SVt_PVIV);
		SvIVX(ST(0)) = err;
		SvIOK_on(ST(0));
	}

void
krb5_init_context()

	CODE:
	if (context) croak("Authen::Krb5 already initialized");
	err = krb5_init_context(&context);
	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;

void
krb5_free_context()

	CODE:
	if (!context) croak("Authen::Krb5 not yet initialized");
	krb5_free_context(context);

void
krb5_init_ets()

	CODE:
#if KRB5_DEPRECATED
	krb5_init_ets(context);
#endif /* KRB5_DEPRECATED */
	XSRETURN_YES;

void
krb5_get_default_realm()

	PREINIT:
	char *realm;

	PPCODE:
	err = krb5_get_default_realm(context,&realm);
	if (err || !realm) XSRETURN_UNDEF;
	XPUSHs(sv_2mortal(newSVpv(realm,strlen(realm))));
	Safefree(realm);

void
krb5_get_host_realm(host)
	char *host

	PREINIT:
	char **realmlist;
	int i;

	PPCODE:
	err = krb5_get_host_realm(context,host,&realmlist);
	if (err || !realmlist) XSRETURN_UNDEF;
	for (i = 0; realmlist[i]; i++) {
		XPUSHs(sv_2mortal(newSVpv(realmlist[i],
			strlen(realmlist[i]))));
	}
	krb5_free_host_realm(context,realmlist);

void
krb5_get_krbhst(realm)
	char *realm

	PREINIT:
	krb5_data realm_data;
	char **hostlist;
	int i;

	PPCODE:
	realm_data.data = realm;
	realm_data.length = strlen(realm);
	err = krb5_get_krbhst(context,&realm_data,&hostlist);
	if (err || !hostlist) XSRETURN_UNDEF;
	for (i = 0; hostlist[i]; i++) {
		XPUSHs(sv_2mortal(newSVpv(hostlist[i],
			strlen(hostlist[i]))));
	}
	krb5_free_krbhst(context,hostlist);

Authen::Krb5::Principal
krb5_build_principal_ext(p)
	Authen::Krb5::Principal p

	CODE:
	err = krb5_build_principal_ext(context, &RETVAL,
		krb5_princ_realm(context, p)->length,
		krb5_princ_realm(context, p)->data,
		KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
		krb5_princ_realm(context, p)->length,
		krb5_princ_realm(context, p)->data,
		0);

	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

Authen::Krb5::Principal
krb5_parse_name(name)
	char *name

	CODE:
	err = krb5_parse_name(context,name,&RETVAL);
	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

Authen::Krb5::Principal
krb5_sname_to_principal(hostname,sname,type)
	char *hostname
	char *sname
	krb5_int32 type

	CODE:
	err = krb5_sname_to_principal(context,hostname,sname,type,&RETVAL);
	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

Authen::Krb5::Ccache
krb5_cc_resolve(string_name)
	char *string_name

	CODE:
	err = krb5_cc_resolve(context, string_name, &RETVAL);
	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

char *
krb5_cc_default_name()

	CODE:
	RETVAL = (char *)krb5_cc_default_name(context);

	OUTPUT:
	RETVAL

Authen::Krb5::Ccache
krb5_cc_default()

	CODE:
	err = krb5_cc_default(context, &RETVAL);
	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

Authen::Krb5::Keytab
krb5_kt_resolve(string_name)
	char *string_name

	CODE:
	err = krb5_kt_resolve(context, string_name, &RETVAL);
	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

char *
krb5_kt_default_name()
        CODE:
        char name[BUFSIZ];
        err = krb5_kt_default_name(context, name, sizeof name - 1);
	if (err)
                XSRETURN_UNDEF;
        name[sizeof name - 1] = '\0';
	ST(0) = sv_2mortal(newSVpv(name, 0));

Authen::Krb5::Keytab
krb5_kt_default()
        CODE:
        err = krb5_kt_default(context, &RETVAL);
	if (err)
                XSRETURN_UNDEF;

        OUTPUT:
        RETVAL

Authen::Krb5::Keyblock
krb5_kt_read_service_key(name, principal, kvno = 0, enctype = 0)
        char *name
        Authen::Krb5::Principal principal
        krb5_kvno kvno
        krb5_enctype enctype

        CODE:
        err = krb5_kt_read_service_key(context, name, principal, kvno, enctype,
            &RETVAL);
        if (err)
                XSRETURN_UNDEF;
        can_free((SV *)RETVAL);

        OUTPUT:
        RETVAL

void
krb5_get_in_tkt_with_password(client, server, password, cc)
	Authen::Krb5::Principal client
	Authen::Krb5::Principal server
	char *password
	Authen::Krb5::Ccache cc

	PREINIT:
	krb5_creds cr;
	krb5_timestamp now;
	krb5_deltat lifetime = 0;

	CODE:
	memset((char *)&cr,0,sizeof(krb5_creds));
	krb5_timeofday(context, &now);
	cr.client = client;
	cr.server = server;
	cr.times.starttime = now;
	cr.times.endtime = now + KRB5_DEFAULT_LIFE;
	cr.times.renew_till = 0;

	err = krb5_get_in_tkt_with_password(context, 0, 0, NULL, NULL,
		password, cc, &cr, 0);

	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;

void
krb5_get_in_tkt_with_keytab(client, server, keytab, cc)
	Authen::Krb5::Principal client
	Authen::Krb5::Principal server
	Authen::Krb5::Keytab    keytab
	Authen::Krb5::Ccache cc

	PREINIT:
	krb5_creds cr;
	krb5_timestamp now;
	krb5_deltat lifetime = 0;

	CODE:
	memset((char *)&cr,0,sizeof(krb5_creds));
	krb5_timeofday(context, &now);
	cr.client = client;
	cr.server = server;
	cr.times.starttime = now;
	cr.times.endtime = now + KRB5_DEFAULT_LIFE;
	cr.times.renew_till = 0;

	err = krb5_get_in_tkt_with_keytab(context, 0, 0, NULL, NULL,
		keytab, cc, &cr, 0);

	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;

SV *
krb5_mk_req(auth_context, ap_req_options, service, hostname, in, cc)
	Authen::Krb5::AuthContext auth_context
	krb5_flags ap_req_options
	char *service
	char *hostname
	SV *in
	Authen::Krb5::Ccache cc

	PREINIT:
	krb5_data in_data, out_data;

	CODE:
	in_data.data = SvPV(in,in_data.length);
	err = krb5_mk_req(context,&auth_context,ap_req_options,service,hostname,
		&in_data,cc,&out_data);
	if (err) XSRETURN_UNDEF;
	RETVAL = newSVpv(out_data.data,out_data.length);

	OUTPUT:
	RETVAL

Authen::Krb5::Ticket
krb5_rd_req(auth_context,in,server,keytab=0)
	Authen::Krb5::AuthContext auth_context
	SV *in
	Authen::Krb5::Principal server
	Authen::Krb5::Keytab keytab

	PREINIT:
	krb5_data in_data;
	krb5_ticket *t;
	krb5_flags ap_req_options;

	CODE:
	if (!New(0,t,1,krb5_ticket)) XSRETURN_UNDEF;
	in_data.data = SvPV(in,in_data.length);
	err = krb5_rd_req(context,&auth_context,&in_data,server,keytab,
		NULL,&t);
	if (err) XSRETURN_UNDEF;
	RETVAL = t;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

Authen::Krb5::Address
gen_portaddr(addr,port)
	Authen::Krb5::Address addr
	unsigned short port

	CODE:
	err = krb5_gen_portaddr(context,addr,(krb5_pointer)&port,&RETVAL);
	if (err) XSRETURN_UNDEF;

	OUTPUT:
	RETVAL

void
genaddrs(auth_context,fh,flags)
	Authen::Krb5::AuthContext auth_context
	FILE *fh; 
	krb5_flags flags

	PREINIT:
	int fd;

	CODE:
	fd = fileno(fh);
	err = krb5_auth_con_genaddrs(context,auth_context,fd,flags);
	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;  

char *
gen_replay_name(addr,uniq)
	Authen::Krb5::Address addr
	char *uniq

	CODE:
	err = krb5_gen_replay_name(context,addr,uniq,&RETVAL);
	if (err) XSRETURN_UNDEF;

	OUTPUT:
	RETVAL
	
void
krb5_mk_priv(auth_context,in)
	Authen::Krb5::AuthContext auth_context
	SV *in

	PREINIT:
	krb5_data in_data, out_data;

	PPCODE:
	in_data.data = SvPV(in,in_data.length);
	err = krb5_mk_priv(context,auth_context,&in_data,&out_data,NULL);
	if (err) XSRETURN_UNDEF;
	XPUSHs(sv_2mortal(newSVpv(out_data.data,out_data.length)));
	/* krb5_free_data(context,&out_data); */

void
krb5_rd_priv(auth_context,in)
	Authen::Krb5::AuthContext auth_context
	SV *in

	PREINIT:
	krb5_data in_data, out_data;

	PPCODE:
	in_data.data = SvPV(in,in_data.length);
	err = krb5_rd_priv(context,auth_context,&in_data,&out_data,NULL);
	if (err) XSRETURN_UNDEF;
	XPUSHs(sv_2mortal(newSVpv(out_data.data,out_data.length)));

Authen::Krb5::Rcache
krb5_get_server_rcache(piece)
	SV *piece

	PREINIT:
	krb5_data rc_data;

	CODE:
	rc_data.data=SvPV(piece,rc_data.length);
	err = krb5_get_server_rcache(context,&rc_data,&RETVAL);

	if (err) XSRETURN_UNDEF;

	OUTPUT:
	RETVAL

void
krb5_sendauth(auth_context,fh,version,client,server,options,in,in_creds,cc)
	Authen::Krb5::AuthContext auth_context
	FILE *fh
	char *version
	Authen::Krb5::Principal client
	Authen::Krb5::Principal server
	int options
	SV *in
	Authen::Krb5::Creds in_creds
	Authen::Krb5::Ccache cc

	PREINIT:
	krb5_data in_data;
	krb5_creds *out_creds = NULL;
	int fd;

	PPCODE:
	fd = fileno(fh);
	in_data.data = SvPV(in,in_data.length);
	err = krb5_sendauth(context,&auth_context,&fd,version,client,server,
		options,&in_data,in_creds,cc,NULL,NULL,&out_creds);
	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;

void
krb5_recvauth(auth_context,fh,version,server,keytab)
	Authen::Krb5::AuthContext auth_context
	FILE *fh
	char *version
	Authen::Krb5::Principal server
	Authen::Krb5::Keytab keytab

	PREINIT:
	krb5_ticket *ticket = NULL;
	int fd;

	PPCODE:
	fd = fileno(fh);
	err = krb5_recvauth(context,&auth_context,&fd,version,server,0,
		keytab,&ticket);
	if (err) XSRETURN_UNDEF;
	ST(0) = sv_newmortal();
	sv_setref_pv(ST(0),"Authen::Krb5::Ticket",(void*)ticket);
	XSRETURN(1);


MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::Principal

void
realm(p)
	Authen::Krb5::Principal p

	CODE:
	ST(0) = sv_2mortal(newSVpv(p->realm.data,p->realm.length));

krb5_int32
type(p)
	Authen::Krb5::Principal p

	CODE:
	RETVAL = p->type;

	OUTPUT:
	RETVAL

void
data(p)
	Authen::Krb5::Principal p

	PPCODE:
	if (p->length > 0) {
		int len = p->length;
		krb5_data *data;

		EXTEND(sp,len);
		for (data = p->data; len--; data++) {
			PUSHs(sv_2mortal(newSVpv(data->data,data->length)));
		}
	}

void
DESTROY(p)
	Authen::Krb5::Principal p

	CODE:
	if (p && should_free((SV *)p)) {
		krb5_free_principal(context,p);
		freed((SV *)p);
	}


MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::Ccache

void
initialize(cc, p)
	Authen::Krb5::Ccache cc
	Authen::Krb5::Principal p

	CODE:
	err = krb5_cc_initialize(context, cc, p);
	if (err) XSRETURN_UNDEF;
	else {
		can_free((SV *)cc);
		XSRETURN_YES;
	}

const char *
get_name(cc)
	Authen::Krb5::Ccache cc

	CODE:
	RETVAL = krb5_cc_get_name(context, cc);

	OUTPUT:
	RETVAL

Authen::Krb5::Principal
get_principal(cc)
	Authen::Krb5::Ccache cc

	CODE:
	err = krb5_cc_get_principal(context, cc, &RETVAL);
	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

void
destroy(cc)
	Authen::Krb5::Ccache cc

	CODE:
	if (!should_free((SV*)cc)) XSRETURN_UNDEF;

	err = krb5_cc_destroy(context, cc);
	if (err) {
		XSRETURN_UNDEF;
	}
	else {
		freed((SV*)cc);
		XSRETURN_YES;
	}

krb5_cc_cursor *
start_seq_get(cc)
	Authen::Krb5::Ccache cc

	CODE:
	if (!New(0, RETVAL, 1, krb5_cc_cursor))
		XSRETURN_UNDEF;
        err = krb5_cc_start_seq_get(context, cc, RETVAL);
	if (err)
                XSRETURN_UNDEF;

        OUTPUT:
        RETVAL

Authen::Krb5::Creds
next_cred(cc, cursor)
        krb5_cc_cursor *cursor
        Authen::Krb5::Ccache cc

        CODE:
	if (!New(0, RETVAL, 1, krb5_creds))
		XSRETURN_UNDEF;
        err = krb5_cc_next_cred(context, cc, cursor, RETVAL);
	if (err)
                XSRETURN_UNDEF;
        can_free((SV *)RETVAL);

        OUTPUT:
        RETVAL

void
end_seq_get(cc, cursor)
        Authen::Krb5::Ccache cc
        krb5_cc_cursor *cursor

        CODE:
        err = krb5_cc_end_seq_get(context, cc, cursor);
        if (err)
                XSRETURN_UNDEF;
        XSRETURN_YES;

void
DESTROY(cc)
	Authen::Krb5::Ccache cc

	CODE:
	if (should_free((SV *)cc)) {
		krb5_cc_close(context, cc);
		freed((SV *)cc);
	}

MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::KeyBlock

int
length(kb)
	Authen::Krb5::KeyBlock kb

	CODE:
	RETVAL = kb->length;

	OUTPUT:
	RETVAL

void
contents(kb)
	Authen::Krb5::KeyBlock kb

	PPCODE:
	/* sv_2mortal here causes 'Attempt to free unreferenced scalar' later */
	XPUSHs(newSVpvn((char*)(kb->contents), kb->length));

int
enctype(kb)
	Authen::Krb5::KeyBlock kb

	CODE:
	RETVAL = (int)kb->enctype;

	OUTPUT:
	RETVAL

void
enctype_string(kb)
	Authen::Krb5::KeyBlock kb

	PREINIT:
	char buf[256];

	PPCODE:
	err = krb5_enctype_to_string(kb->enctype, buf, 255);
	if (err) {
		XSRETURN_UNDEF;
	}
	XPUSHs(newSVpv(buf, 0));

void
DESTROY(kb)
	Authen::Krb5::KeyBlock kb

	CODE:
	if (kb && should_free((SV *)kb)) {
		krb5_free_keyblock(context,kb);
		freed((SV *)kb);
	}

MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::AuthContext

Authen::Krb5::AuthContext
new(class)
	char *class

	CODE:
	err = krb5_auth_con_init(context, &RETVAL);
	if (err) XSRETURN_UNDEF;

	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

int
getflags(auth_context)
	Authen::Krb5::AuthContext auth_context

	PREINIT:
	krb5_int32 flags;

	CODE:
	err = krb5_auth_con_getflags(context,auth_context,&flags);
	RETVAL = (int)flags;

	OUTPUT:
	RETVAL

void
setflags(auth_context,flags)
	Authen::Krb5::AuthContext auth_context
	krb5_int32 flags

	CODE:
	err = krb5_auth_con_setflags(context,auth_context,flags);

	if(err) XSRETURN_UNDEF;
	XSRETURN_YES;

Authen::Krb5::Rcache
getrcache(auth_context)
	Authen::Krb5::AuthContext auth_context

	CODE:
	err = krb5_auth_con_getrcache(context,auth_context,&RETVAL);
	if (err) XSRETURN_UNDEF;

	OUTPUT:
	RETVAL

void
setrcache(auth_context,rc)
	Authen::Krb5::AuthContext auth_context
	Authen::Krb5::Rcache rc

	CODE:
	err = krb5_auth_con_setrcache(context,auth_context,rc);
	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;

void
getaddrs(auth_context)
	Authen::Krb5::AuthContext auth_context

	PREINIT:
	krb5_address *local, *remote;

	CODE:
	err = krb5_auth_con_getaddrs(context,auth_context,&local,&remote);
	if (err) XSRETURN_EMPTY;

	ST(0) = sv_newmortal();
	ST(1) = sv_newmortal();
	sv_setref_pv(ST(0), "Authen::Krb5::Address", (void*)local);
	sv_setref_pv(ST(1), "Authen::Krb5::Address", (void*)remote);
	XSRETURN(2);

void
setaddrs(auth_context,laddr,raddr)
	Authen::Krb5::AuthContext auth_context
	Authen::Krb5::Address laddr
	Authen::Krb5::Address raddr

	CODE:
	if (!SvOK((SV*)ST(1))) laddr = NULL;
	if (!SvOK((SV*)ST(2))) raddr = NULL;
	err = krb5_auth_con_setaddrs(context,auth_context,laddr,raddr);
	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;

void
setports(auth_context,laddr,raddr)
	Authen::Krb5::AuthContext auth_context
	Authen::Krb5::Address laddr
	Authen::Krb5::Address raddr

	CODE:
	if (!SvOK((SV*)ST(1))) laddr = NULL;
	if (!SvOK((SV*)ST(2))) raddr = NULL;
	err = krb5_auth_con_setports(context,auth_context,laddr,raddr);
	if (err) XSRETURN_UNDEF;
	XSRETURN_YES;

Authen::Krb5::KeyBlock
getkey(auth_context)
	Authen::Krb5::AuthContext auth_context;

	PREINIT:
	SV *sv;

	CODE:
	err = krb5_auth_con_getkey(context, auth_context, &RETVAL);
	if (err) XSRETURN_UNDEF;
	can_free((SV *)RETVAL);

	OUTPUT:
	RETVAL

void
DESTROY(auth_context)
	Authen::Krb5::AuthContext auth_context;

	CODE:
	if (auth_context) {
		krb5_auth_con_free(context, auth_context);
		freed((SV *)auth_context);
	}

MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::Ticket

Authen::Krb5::Principal
server(t)
	Authen::Krb5::Ticket t

	CODE:
	RETVAL = t->server;

	OUTPUT:
	RETVAL

Authen::Krb5::EncTktPart
enc_part2(t)
	Authen::Krb5::Ticket t

	CODE:
	RETVAL = t->enc_part2;

	OUTPUT:
	RETVAL

void
DESTROY(t)
	Authen::Krb5::Ticket t

	CODE:
	if (t) {
		krb5_free_ticket(context,t);
		freed((SV *)t);
	}

MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::EncTktPart

Authen::Krb5::Principal
client(etp)
	Authen::Krb5::EncTktPart etp

	CODE:
	RETVAL = etp->client;

	OUTPUT:
	RETVAL

void
DESTROY(etp)
	Authen::Krb5::EncTktPart etp

	CODE:
	if (etp && should_free((SV *)etp)) {
		krb5_free_enc_tkt_part(context,etp);
		freed((SV *)etp);
	}

MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::Address

Authen::Krb5::Address
new(class,addrtype,contents)
	char *class
	unsigned int addrtype
	SV *contents

	CODE:
	if (!New(0,RETVAL,1,krb5_address)) XSRETURN_UNDEF;
	RETVAL->addrtype = addrtype;
	RETVAL->contents = (krb5_octet *)SvPV(contents,RETVAL->length);
	
	OUTPUT:
	RETVAL

void
DESTROY(addr)
	Authen::Krb5::Address addr

	CODE:
	if (addr && should_free((SV *)addr)) {
		krb5_free_address(context,addr);
		freed((SV *)addr);
	}

MODULE = Authen::Krb5	PACKAGE = Authen::Krb5::Keyblock

 
krb5_enctype
enctype(keyblock)
	Authen::Krb5::Keyblock keyblock

        CODE:
        RETVAL = keyblock->enctype;

        OUTPUT:
        RETVAL
 
unsigned int
length(keyblock)
        Authen::Krb5::Keyblock keyblock

        CODE:
        RETVAL = keyblock->length;

        OUTPUT:
        RETVAL

krb5_octet *
contents(keyblock)
        Authen::Krb5::Keyblock keyblock

        CODE:
        ST(0) = keyblock->contents
	    ? sv_2mortal(newSVpv(keyblock->contents, keyblock->length))
            : &PL_sv_undef;


void
DESTROY(keyblock)
	Authen::Krb5::Keyblock	keyblock

        CODE:
	if (keyblock->contents) {
		memset(keyblock->contents, 0, keyblock->length);
		free(keyblock->contents);
		keyblock->contents = NULL;
	}

MODULE = Authen::Krb5   PACKAGE = Authen::Krb5::Keytab

void
add_entry(keytab, entry)
        Authen::Krb5::Keytab keytab
        Authen::Krb5::KeytabEntry entry

        CODE:
        err = krb5_kt_add_entry(context, keytab, entry);
	if (err)
                XSRETURN_UNDEF;
	XSRETURN_YES;

void
end_seq_get(keytab, cursor)
        Authen::Krb5::Keytab keytab
        krb5_kt_cursor *cursor

        CODE:
        err = krb5_kt_end_seq_get(context, keytab, cursor);
        if (err)
                XSRETURN_UNDEF;
        XSRETURN_YES;

Authen::Krb5::KeytabEntry
get_entry(keytab, principal, vno = 0, enctype = 0)
        Authen::Krb5::Keytab keytab
        Authen::Krb5::Principal principal
        krb5_kvno vno
        krb5_enctype enctype

        CODE:
	if (!New(0, RETVAL, 1, krb5_keytab_entry))
		XSRETURN_UNDEF;
        err = krb5_kt_get_entry(context, keytab, principal, vno, enctype,
            RETVAL);
	if (err)
		XSRETURN_UNDEF;
        can_free((SV *)RETVAL);
	
	OUTPUT:
	RETVAL
        
SV *
get_name(keytab)
        Authen::Krb5::Keytab keytab

        PREINIT:
        char name[MAX_KEYTAB_NAME_LEN+1];

        CODE:
        err = krb5_kt_get_name(context, keytab, name, MAX_KEYTAB_NAME_LEN);
	if (err)
                XSRETURN_UNDEF;
	RETVAL = sv_2mortal(newSVpv(name, 0));
        can_free((SV *)RETVAL);

        OUTPUT:
        RETVAL

Authen::Krb5::KeytabEntry
next_entry(keytab, cursor)
        krb5_kt_cursor *cursor
        Authen::Krb5::Keytab keytab

        CODE:
	if (!New(0, RETVAL, 1, krb5_keytab_entry))
		XSRETURN_UNDEF;
        err = krb5_kt_next_entry(context, keytab, RETVAL, cursor);
	if (err)
                XSRETURN_UNDEF;
        can_free((SV *)RETVAL);

        OUTPUT:
        RETVAL

void
remove_entry(keytab, entry)
        Authen::Krb5::Keytab keytab
        Authen::Krb5::KeytabEntry entry

        CODE:
        err = krb5_kt_remove_entry(context, keytab, entry);
	if (err)
                XSRETURN_UNDEF;
	XSRETURN_YES;

krb5_kt_cursor *
start_seq_get(keytab)
        Authen::Krb5::Keytab keytab

        CODE:
	if (!New(0, RETVAL, 1, krb5_kt_cursor))
		XSRETURN_UNDEF;
        err = krb5_kt_start_seq_get(context, keytab, RETVAL);
	if (err)
                XSRETURN_UNDEF;

        OUTPUT:
        RETVAL

void
DESTROY(keytab)
        Authen::Krb5::Keytab keytab

        CODE:
        if (keytab && should_free((SV *)keytab)) {
                krb5_kt_close(context, keytab);
                freed((SV *)keytab);
        }

MODULE = Authen::Krb5   PACKAGE = Authen::Krb5::KeytabEntry

Authen::Krb5::KeytabEntry
new(class, principal, vno, key)
	char *class
	Authen::Krb5::Principal principal
	krb5_kvno vno
        Authen::Krb5::Keyblock key

	CODE:
	if (!New(0, RETVAL, 1, krb5_keytab_entry))
		XSRETURN_UNDEF;
        *RETVAL = keytab_entry_init;
	RETVAL->principal = principal;
	RETVAL->vno = vno;
        RETVAL->key = *key;

        can_free((SV *)RETVAL);
	
	OUTPUT:
	RETVAL

Authen::Krb5::Principal
principal(entry)
	Authen::Krb5::KeytabEntry entry

        CODE:
        err = krb5_copy_principal(context, entry->principal, &RETVAL);
        if (err)
                XSRETURN_UNDEF;
        can_free((SV *)RETVAL);

        OUTPUT:
        RETVAL

krb5_timestamp
timestamp(entry)
	Authen::Krb5::KeytabEntry entry

        CODE:
        RETVAL = entry->timestamp;

        OUTPUT:
        RETVAL
 
krb5_kvno
kvno(entry)
	Authen::Krb5::KeytabEntry entry

        CODE:
        RETVAL = entry->vno;

        OUTPUT:
        RETVAL
 
Authen::Krb5::Keyblock
key(entry)
	Authen::Krb5::KeytabEntry entry

        CODE:
        err = krb5_copy_keyblock(context, &entry->key, &RETVAL);
        if (err)
                XSRETURN_UNDEF;
        can_free((SV *)RETVAL);

        OUTPUT:
        RETVAL