mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-24 16:10:02 -06:00
724 lines
22 KiB
C
724 lines
22 KiB
C
|
/*
|
||
|
* Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||
|
*/
|
||
|
#include <gen_ndr/ndr_krb5pac.h>
|
||
|
#include <gssapi/gssapi_ext.h>
|
||
|
#include <gssapi/gssapi_krb5.h>
|
||
|
#include <ndr.h>
|
||
|
#include <popt.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <strings.h>
|
||
|
|
||
|
#define IPAPWD_PASSWORD_MAX_LEN 1024
|
||
|
|
||
|
typedef enum {
|
||
|
OP_SERVICE_TICKET,
|
||
|
OP_IMPERSONATE
|
||
|
} pac_operation_t;
|
||
|
|
||
|
pac_operation_t operation = OP_SERVICE_TICKET;
|
||
|
char *keytab_path = NULL;
|
||
|
char *ccache_path = NULL;
|
||
|
bool init_tgt = true;
|
||
|
const gss_OID *import_name_oid = &GSS_C_NT_USER_NAME;
|
||
|
|
||
|
TALLOC_CTX *frame = NULL;
|
||
|
|
||
|
gss_OID_desc mech_krb5 = {9, "\052\206\110\206\367\022\001\002\002"};
|
||
|
|
||
|
/* NDR printing interface passes flags but the actual public print function
|
||
|
* does not accept flags. Generated ndr helpers actually have a small wrapper
|
||
|
* but since it is a static to the generated C code unit, we have to reimplement
|
||
|
* it here.
|
||
|
*/
|
||
|
static void
|
||
|
print_flags_PAC_DATA(struct ndr_print *ndr,
|
||
|
const char *name,
|
||
|
int unused,
|
||
|
const struct PAC_DATA *r)
|
||
|
{
|
||
|
ndr_print_PAC_DATA(ndr, name, r);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Print content of a PAC buffer, annotated by the libndr helpers
|
||
|
*/
|
||
|
static void
|
||
|
print_pac(gss_buffer_desc *pac, gss_buffer_desc *display)
|
||
|
{
|
||
|
struct ndr_print *ndr = NULL;
|
||
|
DATA_BLOB blob;
|
||
|
struct ndr_pull *ndr_pull = NULL;
|
||
|
void *st = NULL;
|
||
|
int flags = NDR_SCALARS | NDR_BUFFERS;
|
||
|
enum ndr_err_code ndr_err;
|
||
|
struct ndr_interface_call ndr_call = {
|
||
|
.name = "PAC_DATA",
|
||
|
.struct_size = sizeof(struct PAC_DATA),
|
||
|
.ndr_push = (ndr_push_flags_fn_t)ndr_push_PAC_DATA,
|
||
|
.ndr_pull = (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA,
|
||
|
.ndr_print = (ndr_print_function_t)print_flags_PAC_DATA,
|
||
|
};
|
||
|
|
||
|
ndr = talloc_zero(frame, struct ndr_print);
|
||
|
ndr->print = ndr_print_string_helper;
|
||
|
ndr->depth = 0;
|
||
|
|
||
|
blob = data_blob_const(pac->value, pac->length);
|
||
|
ndr_pull = ndr_pull_init_blob(&blob, ndr);
|
||
|
ndr_pull->flags = LIBNDR_FLAG_REF_ALLOC;
|
||
|
|
||
|
st = talloc_zero_size(ndr, ndr_call.struct_size);
|
||
|
ndr_err = ndr_call.ndr_pull(ndr_pull, flags, st);
|
||
|
if (ndr_err) {
|
||
|
fprintf(stderr,
|
||
|
"Error parsing buffer '%.*s': %s\n",
|
||
|
(int)display->length,
|
||
|
(char *)display->value,
|
||
|
ndr_map_error2string(ndr_err));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ndr_call.ndr_print(ndr, ndr_call.name, flags, st);
|
||
|
printf("%s\n", (char *)ndr->private_data);
|
||
|
talloc_free(ndr);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
display_error(int type, OM_uint32 code)
|
||
|
{
|
||
|
OM_uint32 min, ctx = 0;
|
||
|
gss_buffer_desc status;
|
||
|
|
||
|
do {
|
||
|
(void)gss_display_status(&min, code, type, GSS_C_NO_OID, &ctx, &status);
|
||
|
fprintf(stderr, "%.*s\n", (int)status.length, (char *)status.value);
|
||
|
gss_release_buffer(&min, &status);
|
||
|
} while (ctx != 0);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
log_error(const char *fn, uint32_t maj, uint32_t min)
|
||
|
{
|
||
|
fprintf(stderr, "%s: ", fn);
|
||
|
display_error(GSS_C_GSS_CODE, maj);
|
||
|
display_error(GSS_C_MECH_CODE, min);
|
||
|
}
|
||
|
|
||
|
static gss_name_t
|
||
|
import_name(const char *name)
|
||
|
{
|
||
|
OM_uint32 maj, min;
|
||
|
gss_name_t gss_name;
|
||
|
gss_name = GSS_C_NO_NAME;
|
||
|
gss_buffer_desc buff = GSS_C_EMPTY_BUFFER;
|
||
|
|
||
|
buff.value = (void *)name;
|
||
|
buff.length = strlen(name);
|
||
|
|
||
|
maj = gss_import_name(&min, &buff, *import_name_oid, &gss_name);
|
||
|
if (GSS_ERROR(maj)) {
|
||
|
log_error("gss_import_name()", maj, min);
|
||
|
return GSS_C_NO_NAME;
|
||
|
}
|
||
|
|
||
|
return gss_name;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
store_creds_into_cache(gss_cred_id_t creds, const char *cache)
|
||
|
{
|
||
|
OM_uint32 maj, min;
|
||
|
gss_key_value_element_desc store_elm = {"ccache", cache};
|
||
|
gss_key_value_set_desc store = {1, &store_elm};
|
||
|
|
||
|
maj = gss_store_cred_into(
|
||
|
&min, creds, GSS_C_INITIATE, GSS_C_NO_OID, 1, 1, &store, NULL, NULL);
|
||
|
if (maj != GSS_S_COMPLETE) {
|
||
|
log_error("gss_store_cred_into()", maj, min);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
dump_attribute(gss_name_t name, gss_buffer_t attribute)
|
||
|
{
|
||
|
OM_uint32 major, minor;
|
||
|
gss_buffer_desc value;
|
||
|
gss_buffer_desc display_value;
|
||
|
int authenticated = 0;
|
||
|
int complete = 0;
|
||
|
int more = -1;
|
||
|
int whole_pac = 0;
|
||
|
|
||
|
whole_pac = attribute->length == strlen("urn:mspac:");
|
||
|
while (more != 0) {
|
||
|
value.value = NULL;
|
||
|
display_value.value = NULL;
|
||
|
|
||
|
major = gss_get_name_attribute(&minor,
|
||
|
name,
|
||
|
attribute,
|
||
|
&authenticated,
|
||
|
&complete,
|
||
|
&value,
|
||
|
&display_value,
|
||
|
&more);
|
||
|
if (GSS_ERROR(major)) {
|
||
|
log_error("gss_get_name_attribute()", major, minor);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (whole_pac) {
|
||
|
print_pac(&value, attribute);
|
||
|
}
|
||
|
|
||
|
(void)gss_release_buffer(&minor, &value);
|
||
|
(void)gss_release_buffer(&minor, &display_value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
enumerate_attributes(gss_name_t name)
|
||
|
{
|
||
|
OM_uint32 major, minor;
|
||
|
int is_mechname;
|
||
|
gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
|
||
|
size_t i;
|
||
|
|
||
|
major = gss_inquire_name(&minor, name, &is_mechname, NULL, &attrs);
|
||
|
if (GSS_ERROR(major)) {
|
||
|
log_error("gss_inquire_name()", major, minor);
|
||
|
return;
|
||
|
}
|
||
|
if (GSS_ERROR(major)) {
|
||
|
printf("gss_inquire_name: (%d, %d)\n", major, minor);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (attrs != GSS_C_NO_BUFFER_SET) {
|
||
|
for (i = 0; i < attrs->count; i++)
|
||
|
dump_attribute(name, &attrs->elements[i]);
|
||
|
}
|
||
|
|
||
|
(void)gss_release_buffer_set(&minor, &attrs);
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
establish_contexts(gss_OID imech,
|
||
|
gss_cred_id_t icred,
|
||
|
gss_cred_id_t acred,
|
||
|
gss_name_t tname,
|
||
|
OM_uint32 flags,
|
||
|
gss_ctx_id_t *ictx,
|
||
|
gss_ctx_id_t *actx,
|
||
|
gss_name_t *src_name,
|
||
|
gss_OID *amech,
|
||
|
gss_cred_id_t *deleg_cred)
|
||
|
{
|
||
|
OM_uint32 minor, imaj, amaj;
|
||
|
gss_buffer_desc itok, atok;
|
||
|
|
||
|
*ictx = *actx = GSS_C_NO_CONTEXT;
|
||
|
imaj = amaj = GSS_S_CONTINUE_NEEDED;
|
||
|
itok.value = atok.value = NULL;
|
||
|
itok.length = atok.length = 0;
|
||
|
for (;;) {
|
||
|
(void)gss_release_buffer(&minor, &itok);
|
||
|
imaj = gss_init_sec_context(&minor,
|
||
|
icred,
|
||
|
ictx,
|
||
|
tname,
|
||
|
imech,
|
||
|
flags,
|
||
|
GSS_C_INDEFINITE,
|
||
|
GSS_C_NO_CHANNEL_BINDINGS,
|
||
|
&atok,
|
||
|
NULL,
|
||
|
&itok,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
if (GSS_ERROR(imaj)) {
|
||
|
log_error("gss_init_sec_context()", imaj, minor);
|
||
|
return false;
|
||
|
}
|
||
|
if (amaj == GSS_S_COMPLETE)
|
||
|
break;
|
||
|
|
||
|
(void)gss_release_buffer(&minor, &atok);
|
||
|
amaj = gss_accept_sec_context(&minor,
|
||
|
actx,
|
||
|
acred,
|
||
|
&itok,
|
||
|
GSS_C_NO_CHANNEL_BINDINGS,
|
||
|
src_name,
|
||
|
amech,
|
||
|
&atok,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
deleg_cred);
|
||
|
if (GSS_ERROR(amaj)) {
|
||
|
log_error("gss_accept_sec_context()", amaj, minor);
|
||
|
return false;
|
||
|
}
|
||
|
(void)gss_release_buffer(&minor, &itok);
|
||
|
if (imaj == GSS_S_COMPLETE) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (imaj != GSS_S_COMPLETE || amaj != GSS_S_COMPLETE) {
|
||
|
printf("One side wants to continue after the other is done");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
(void)gss_release_buffer(&minor, &itok);
|
||
|
(void)gss_release_buffer(&minor, &atok);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
init_accept_sec_context(gss_cred_id_t claimant_cred_handle,
|
||
|
gss_cred_id_t verifier_cred_handle,
|
||
|
gss_cred_id_t *deleg_cred_handle)
|
||
|
{
|
||
|
OM_uint32 maj, min, flags;
|
||
|
gss_name_t source_name = GSS_C_NO_NAME, target_name = GSS_C_NO_NAME;
|
||
|
gss_ctx_id_t initiator_context, acceptor_context;
|
||
|
gss_OID mech = &mech_krb5;
|
||
|
bool success = false;
|
||
|
|
||
|
maj = gss_inquire_cred(
|
||
|
&min, verifier_cred_handle, &target_name, NULL, NULL, NULL);
|
||
|
if (GSS_ERROR(maj)) {
|
||
|
log_error("gss_inquire_cred()", maj, min);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
|
||
|
flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
|
||
|
success = establish_contexts(mech,
|
||
|
claimant_cred_handle,
|
||
|
verifier_cred_handle,
|
||
|
target_name,
|
||
|
flags,
|
||
|
&initiator_context,
|
||
|
&acceptor_context,
|
||
|
&source_name,
|
||
|
&mech,
|
||
|
deleg_cred_handle);
|
||
|
if (success)
|
||
|
enumerate_attributes(source_name);
|
||
|
done:
|
||
|
if (source_name != GSS_C_NO_NAME)
|
||
|
(void)gss_release_name(&min, &source_name);
|
||
|
|
||
|
if (target_name != GSS_C_NO_NAME)
|
||
|
(void)gss_release_name(&min, &target_name);
|
||
|
|
||
|
if (initiator_context != NULL)
|
||
|
(void)gss_delete_sec_context(&min, &initiator_context, NULL);
|
||
|
|
||
|
if (acceptor_context != NULL)
|
||
|
(void)gss_delete_sec_context(&min, &acceptor_context, NULL);
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
init_creds(gss_cred_id_t *service_creds, gss_cred_usage_t intent)
|
||
|
{
|
||
|
OM_uint32 maj, min;
|
||
|
gss_key_value_element_desc keytab_elm = {"keytab", keytab_path};
|
||
|
gss_key_value_set_desc store = {1, &keytab_elm};
|
||
|
|
||
|
maj = gss_acquire_cred_from(&min,
|
||
|
GSS_C_NO_NAME,
|
||
|
GSS_C_INDEFINITE,
|
||
|
GSS_C_NO_OID_SET,
|
||
|
intent,
|
||
|
(keytab_path != NULL) ? &store : NULL,
|
||
|
service_creds,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
if (GSS_ERROR(maj)) {
|
||
|
log_error("gss_acquire_cred", maj, min);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
impersonate(const char *name)
|
||
|
{
|
||
|
OM_uint32 maj, min;
|
||
|
gss_name_t desired_principal = GSS_C_NO_NAME;
|
||
|
gss_cred_id_t client_creds = GSS_C_NO_CREDENTIAL;
|
||
|
gss_cred_id_t service_creds = GSS_C_NO_CREDENTIAL;
|
||
|
gss_cred_id_t delegated_creds = GSS_C_NO_CREDENTIAL;
|
||
|
bool success = false;
|
||
|
|
||
|
if (!init_creds(&service_creds, GSS_C_BOTH)) {
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
desired_principal = import_name(name);
|
||
|
if (desired_principal == GSS_C_NO_NAME) {
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
maj = gss_acquire_cred_impersonate_name(&min,
|
||
|
service_creds,
|
||
|
desired_principal,
|
||
|
GSS_C_INDEFINITE,
|
||
|
GSS_C_NO_OID_SET,
|
||
|
GSS_C_INITIATE,
|
||
|
&client_creds,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
if (GSS_ERROR(maj)) {
|
||
|
log_error("gss_acquire_cred_impersonate_name()", maj, min);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
if (ccache_path != NULL) {
|
||
|
if (!store_creds_into_cache(client_creds, ccache_path)) {
|
||
|
fprintf(stderr, "Failed to store credentials in cache\n");
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fprintf(stderr, "Acquired credentials for %s\n", name);
|
||
|
init_accept_sec_context(client_creds, service_creds, &delegated_creds);
|
||
|
|
||
|
if (delegated_creds != GSS_C_NO_CREDENTIAL) {
|
||
|
gss_buffer_set_t bufset = GSS_C_NO_BUFFER_SET;
|
||
|
/* Inquire impersonator status. */
|
||
|
maj = gss_inquire_cred_by_oid(
|
||
|
&min, client_creds, GSS_KRB5_GET_CRED_IMPERSONATOR, &bufset);
|
||
|
if (GSS_ERROR(maj)) {
|
||
|
log_error("gss_inquire_cred_by_oid()", maj, min);
|
||
|
goto done;
|
||
|
}
|
||
|
if (bufset->count == 0) {
|
||
|
log_error("gss_inquire_cred_by_oid(user) returned NO impersonator", 0, 0);
|
||
|
goto done;
|
||
|
}
|
||
|
(void)gss_release_buffer_set(&min, &bufset);
|
||
|
|
||
|
maj = gss_inquire_cred_by_oid(
|
||
|
&min, service_creds, GSS_KRB5_GET_CRED_IMPERSONATOR, &bufset);
|
||
|
if (GSS_ERROR(maj)) {
|
||
|
log_error("gss_inquire_cred_by_oid()", maj, min);
|
||
|
goto done;
|
||
|
}
|
||
|
if (bufset->count != 0) {
|
||
|
log_error("gss_inquire_cred_by_oid(svc) returned an impersonator", 0, 0);
|
||
|
goto done;
|
||
|
}
|
||
|
(void)gss_release_buffer_set(&min, &bufset);
|
||
|
success = true;
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
|
||
|
if (desired_principal != GSS_C_NO_NAME)
|
||
|
gss_release_name(&min, &desired_principal);
|
||
|
|
||
|
if (client_creds != GSS_C_NO_CREDENTIAL)
|
||
|
gss_release_cred(&min, &client_creds);
|
||
|
|
||
|
if (service_creds != GSS_C_NO_CREDENTIAL)
|
||
|
gss_release_cred(&min, &service_creds);
|
||
|
|
||
|
if (delegated_creds != GSS_C_NO_CREDENTIAL)
|
||
|
gss_release_cred(&min, &delegated_creds);
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
init_with_password(const char *name, const char *password)
|
||
|
{
|
||
|
OM_uint32 maj, min;
|
||
|
gss_name_t desired_principal = GSS_C_NO_NAME;
|
||
|
gss_cred_id_t client_creds = GSS_C_NO_CREDENTIAL;
|
||
|
gss_cred_id_t service_creds = GSS_C_NO_CREDENTIAL;
|
||
|
gss_buffer_desc pwd_buf;
|
||
|
bool success = false;
|
||
|
|
||
|
if (!init_creds(&service_creds, GSS_C_ACCEPT)) {
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
desired_principal = import_name(name);
|
||
|
if (desired_principal == GSS_C_NO_NAME) {
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
if (init_tgt && password != NULL) {
|
||
|
pwd_buf.value = (void *)password;
|
||
|
pwd_buf.length = strlen(password);
|
||
|
maj = gss_acquire_cred_with_password(&min,
|
||
|
desired_principal,
|
||
|
&pwd_buf,
|
||
|
GSS_C_INDEFINITE,
|
||
|
GSS_C_NO_OID_SET,
|
||
|
GSS_C_INITIATE,
|
||
|
&client_creds,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
if (GSS_ERROR(maj)) {
|
||
|
log_error("gss_acquire_cred_with_password()", maj, min);
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((ccache_path != NULL) && (client_creds != GSS_C_NO_CREDENTIAL)) {
|
||
|
if (!store_creds_into_cache(client_creds, ccache_path)) {
|
||
|
fprintf(stderr, "Failed to store credentials in cache\n");
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (client_creds != GSS_C_NO_CREDENTIAL)
|
||
|
fprintf(stderr, "Acquired credentials for %s\n", name);
|
||
|
|
||
|
success = init_accept_sec_context(client_creds, service_creds, NULL);
|
||
|
|
||
|
done:
|
||
|
if (service_creds != GSS_C_NO_CREDENTIAL)
|
||
|
gss_release_cred(&min, &client_creds);
|
||
|
|
||
|
if (client_creds != GSS_C_NO_CREDENTIAL)
|
||
|
gss_release_cred(&min, &client_creds);
|
||
|
|
||
|
if (desired_principal != GSS_C_NO_NAME)
|
||
|
gss_release_name(&min, &desired_principal);
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
struct poptOption popt_options[] = {
|
||
|
{
|
||
|
.longName = "enterprise",
|
||
|
.shortName = 'E',
|
||
|
.argInfo = POPT_ARG_NONE | POPT_ARGFLAG_OPTIONAL,
|
||
|
.val = 'E',
|
||
|
.descrip = "Treat the user principal as an enterprise name",
|
||
|
},
|
||
|
{
|
||
|
.longName = "ccache",
|
||
|
.shortName = 'c',
|
||
|
.argInfo = POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL,
|
||
|
.val = 'c',
|
||
|
.descrip = "Credentials cache file to save acquired tickets to. "
|
||
|
"Tickets aren't saved by default",
|
||
|
.argDescrip = "CCACHE-PATH",
|
||
|
},
|
||
|
{
|
||
|
.longName = "keytab",
|
||
|
.shortName = 'k',
|
||
|
.argInfo = POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL,
|
||
|
.val = 'k',
|
||
|
.descrip = "Keytab for a service key to acquire service ticket for. "
|
||
|
"Default keytab is used if omitted",
|
||
|
.argDescrip = "KEYTAB-PATH",
|
||
|
},
|
||
|
{
|
||
|
.longName = "reuse",
|
||
|
.shortName = 'r',
|
||
|
.argInfo = POPT_ARG_NONE | POPT_ARGFLAG_OPTIONAL,
|
||
|
.val = 'r',
|
||
|
.descrip = "Re-use user principal's TGT from a default ccache",
|
||
|
},
|
||
|
{
|
||
|
.longName = "help",
|
||
|
.shortName = 'h',
|
||
|
.argInfo = POPT_ARG_NONE | POPT_ARGFLAG_OPTIONAL,
|
||
|
.val = 'h',
|
||
|
.descrip = "Show this help message",
|
||
|
},
|
||
|
|
||
|
POPT_TABLEEND};
|
||
|
|
||
|
static void
|
||
|
print_help(poptContext pc, const char *name)
|
||
|
{
|
||
|
const char *help = ""
|
||
|
"Usage: %s [options] {impersonate|ticket} user@realm\n\n"
|
||
|
"Print MS-PAC structure from a service ticket.\n\n"
|
||
|
"Operation 'impersonate':\n"
|
||
|
"\tExpects a TGT for a service in the default ccache and attempts to "
|
||
|
"obtain a service\n"
|
||
|
"\tticket to itself by performing a protocol transition for the specified "
|
||
|
"user (S4U2Self).\n\n"
|
||
|
"Operation 'ticket':\n"
|
||
|
"\tExpects a user password to be provided, acquires ticket granting ticket "
|
||
|
"and attempts to \n"
|
||
|
"\tobtain a service ticket to the specified service.\n\n"
|
||
|
"Resulting service ticket can be stored in the credential cache file "
|
||
|
"specified by '-c file' option.\n\n"
|
||
|
"Defaults to the host principal service name and the host keytab.\n\n";
|
||
|
fprintf(stderr, help, name);
|
||
|
poptPrintHelp(pc, stderr, 0);
|
||
|
}
|
||
|
|
||
|
static char *
|
||
|
ask_password(TALLOC_CTX *context, char *prompt1, char *prompt2, bool match)
|
||
|
{
|
||
|
krb5_prompt ap_prompts[2];
|
||
|
krb5_data k5d_pw0;
|
||
|
krb5_data k5d_pw1;
|
||
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||
|
#define PWD_BUFFER_SIZE MAX((IPAPWD_PASSWORD_MAX_LEN + 2), 1024)
|
||
|
char pw0[PWD_BUFFER_SIZE];
|
||
|
char pw1[PWD_BUFFER_SIZE];
|
||
|
char *password;
|
||
|
int num_prompts = match ? 2 : 1;
|
||
|
|
||
|
k5d_pw0.length = sizeof(pw0);
|
||
|
k5d_pw0.data = pw0;
|
||
|
ap_prompts[0].prompt = prompt1;
|
||
|
ap_prompts[0].hidden = 1;
|
||
|
ap_prompts[0].reply = &k5d_pw0;
|
||
|
|
||
|
if (match) {
|
||
|
k5d_pw1.length = sizeof(pw1);
|
||
|
k5d_pw1.data = pw1;
|
||
|
ap_prompts[1].prompt = prompt2;
|
||
|
ap_prompts[1].hidden = 1;
|
||
|
ap_prompts[1].reply = &k5d_pw1;
|
||
|
}
|
||
|
|
||
|
/* krb5_prompter_posix does not use krb5_context internally */
|
||
|
krb5_prompter_posix(NULL, NULL, NULL, NULL, num_prompts, ap_prompts);
|
||
|
|
||
|
if (match && (strcmp(pw0, pw1))) {
|
||
|
fprintf(stderr, "Passwords do not match!\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (k5d_pw0.length > IPAPWD_PASSWORD_MAX_LEN) {
|
||
|
fprintf(stderr, "%s\n", "Password is too long!\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
password = talloc_strndup(context, pw0, k5d_pw0.length);
|
||
|
if (!password)
|
||
|
return NULL;
|
||
|
return password;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
int ret = 0, c = 0;
|
||
|
const char **argv_const = discard_const_p(const char *, argv);
|
||
|
const char **args = NULL;
|
||
|
char *password = NULL;
|
||
|
poptContext pc;
|
||
|
|
||
|
frame = talloc_init("printpac");
|
||
|
pc = poptGetContext(
|
||
|
"printpac", argc, argv_const, popt_options, POPT_CONTEXT_KEEP_FIRST);
|
||
|
while ((c = poptGetNextOpt(pc)) >= 0) {
|
||
|
switch (c) {
|
||
|
case 'c':
|
||
|
ccache_path = talloc_strdup(frame, poptGetOptArg(pc));
|
||
|
break;
|
||
|
case 'E':
|
||
|
import_name_oid = &GSS_KRB5_NT_ENTERPRISE_NAME;
|
||
|
break;
|
||
|
case 'k':
|
||
|
keytab_path = talloc_strdup(frame, poptGetOptArg(pc));
|
||
|
break;
|
||
|
case 'r':
|
||
|
init_tgt = false;
|
||
|
break;
|
||
|
case 'h':
|
||
|
print_help(pc, argv[0]);
|
||
|
ret = 0;
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
if (c < -1) {
|
||
|
fprintf(stderr,
|
||
|
"%s: %s\n",
|
||
|
poptBadOption(pc, POPT_BADOPTION_NOALIAS),
|
||
|
poptStrerror(c));
|
||
|
ret = 1;
|
||
|
goto done;
|
||
|
}
|
||
|
args = poptGetArgs(pc);
|
||
|
for (c = 0; args && args[c]; c++)
|
||
|
;
|
||
|
|
||
|
if (c < 3) {
|
||
|
print_help(pc, args[0]);
|
||
|
ret = 1;
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
c -= 2;
|
||
|
if (strncasecmp("ticket", args[1], strlen("ticket")) == 0) {
|
||
|
operation = OP_SERVICE_TICKET;
|
||
|
if (init_tgt) {
|
||
|
switch (c) {
|
||
|
case 1:
|
||
|
password = ask_password(frame, "Password", NULL, false);
|
||
|
break;
|
||
|
case 2:
|
||
|
password = talloc_strdup(frame, args[3]);
|
||
|
break;
|
||
|
default:
|
||
|
fprintf(stderr,
|
||
|
"Service ticket needs user principal and password\n\n");
|
||
|
print_help(pc, args[0]);
|
||
|
ret = 1;
|
||
|
goto done;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
if (c != 1) {
|
||
|
fprintf(stderr, "Service ticket needs user principal and password\n\n");
|
||
|
print_help(pc, args[0]);
|
||
|
ret = 1;
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
} else if (strncasecmp("impersonate", args[1], strlen("impersonate")) == 0) {
|
||
|
operation = OP_IMPERSONATE;
|
||
|
if (c != 1) {
|
||
|
fprintf(stderr, "Impersonation ticket needs user principal\n\n");
|
||
|
print_help(pc, args[0]);
|
||
|
ret = 1;
|
||
|
goto done;
|
||
|
}
|
||
|
} else {
|
||
|
fprintf(stderr, "Wrong request type: %s\n\n", args[1]);
|
||
|
print_help(pc, args[0]);
|
||
|
ret = 1;
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
switch (operation) {
|
||
|
case OP_IMPERSONATE:
|
||
|
ret = impersonate(args[2]) != true;
|
||
|
break;
|
||
|
case OP_SERVICE_TICKET:
|
||
|
ret = init_with_password(args[2], password) != true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
poptFreeContext(pc);
|
||
|
talloc_free(frame);
|
||
|
return ret;
|
||
|
}
|