Beispiel #1
0
static krb5_error_code
update_keytab_entry(krb5_context context,
		    kcm_ccache ccache,
		    krb5_enctype etype,
		    char *cpn,
		    char *spn,
		    char *newpw,
		    krb5_salt salt,
		    unsigned kvno)
{
    krb5_error_code ret;
    krb5_keytab_entry entry;
    krb5_data pw;

    memset(&entry, 0, sizeof(entry));

    pw.data = (char *)newpw;
    pw.length = strlen(newpw);

    ret = krb5_string_to_key_data_salt(context, etype, pw,
				       salt, &entry.keyblock);
    if (ret) {
	kcm_log(0, "String to key conversion failed for principal %s "
		"and etype %d: %s",
		cpn, etype, krb5_get_err_text(context, ret)); 
	return ret;
    }

    if (spn == NULL) {
	ret = krb5_copy_principal(context, ccache->client,
				  &entry.principal);
	if (ret) {
	    kcm_log(0, "Failed to copy principal name %s: %s",
		    cpn, krb5_get_err_text(context, ret));
	    return ret;
	}
    } else {
	ret = krb5_parse_name(context, spn, &entry.principal);
	if (ret) {
	    kcm_log(0, "Failed to parse SPN alias %s: %s",
		    spn, krb5_get_err_text(context, ret));
	    return ret;
	}
    }

    entry.vno = kvno;
    entry.timestamp = time(NULL);

    ret = krb5_kt_add_entry(context, ccache->key.keytab, &entry);
    if (ret) {
	kcm_log(0, "Failed to update keytab for principal %s "
		"and etype %d: %s",
		cpn, etype, krb5_get_err_text(context, ret));
    }

    krb5_kt_free_entry(context, &entry);

    return ret; 
}
Beispiel #2
0
krb5_error_code
kcm_dispatch(krb5_context context,
	     kcm_client *client,
	     krb5_data *req_data,
	     krb5_data *resp_data)
{
    krb5_error_code ret;
    kcm_method method;
    krb5_storage *req_sp = NULL;
    krb5_storage *resp_sp = NULL;
    u_int16_t opcode;

    resp_sp = krb5_storage_emem();
    if (resp_sp == NULL) {
	return ENOMEM;
    }

    if (client->pid == -1) {
	kcm_log(0, "Client had invalid process number");
	ret = KRB5_FCC_INTERNAL;
	goto out;
    }

    req_sp = krb5_storage_from_data(req_data);
    if (req_sp == NULL) {
	kcm_log(0, "Process %d: failed to initialize storage from data",
		client->pid);
	ret = KRB5_CC_IO;
	goto out;
    }

    krb5_ret_int16(req_sp, &opcode);

    if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) {
	kcm_log(0, "Process %d: invalid operation code %d",
		client->pid, opcode);
	ret = KRB5_FCC_INTERNAL;
	goto out;
    }
    method = kcm_ops[opcode].method;

    /* seek past place for status code */
    krb5_storage_seek(resp_sp, 4, SEEK_SET);

    ret = (*method)(context, client, opcode, req_sp, resp_sp);

out:
    if (req_sp != NULL) {
	krb5_storage_free(req_sp);
    }

    krb5_storage_seek(resp_sp, 0, SEEK_SET);
    krb5_store_int32(resp_sp, ret);

    ret = krb5_storage_to_data(resp_sp, resp_data);
    krb5_storage_free(resp_sp);

    return ret;
}
Beispiel #3
0
krb5_error_code
kcm_ccache_destroy_client(krb5_context context,
			  kcm_client *client,
			  const char *name)
{
    krb5_error_code ret;
    kcm_ccache ccache;
    const char *estr;

    ret = kcm_ccache_resolve(context, name, &ccache);
    if (ret) {
	estr = krb5_get_error_message(context, ret);
	kcm_log(1, "Failed to resolve cache %s: %s", name, estr);
	krb5_free_error_message(context, estr);
	return ret;
    }

    ret = kcm_access(context, client, KCM_OP_DESTROY, ccache);
    kcm_cleanup_events(context, ccache);
    kcm_release_ccache(context, ccache);
    if (ret)
	return ret;

    return kcm_ccache_destroy(context, name);
}
Beispiel #4
0
krb5_error_code
kcm_ccache_resolve_client(krb5_context context,
			  kcm_client *client,
			  kcm_operation opcode,
			  const char *name,
			  kcm_ccache *ccache)
{
    krb5_error_code ret;
    const char *estr;

    ret = kcm_ccache_resolve(context, name, ccache);
    if (ret) {
	estr = krb5_get_error_message(context, ret);
	kcm_log(1, "Failed to resolve cache %s: %s", name, estr);
	krb5_free_error_message(context, estr);
	return ret;
    }

    ret = kcm_access(context, client, opcode, *ccache);
    if (ret) {
	ret = KRB5_FCC_NOFILE; /* don't disclose */
	kcm_release_ccache(context, *ccache);
    }

    return ret;
}
Beispiel #5
0
krb5_error_code
kcm_run_events(krb5_context context,
	       time_t now)
{
    krb5_error_code ret;
    kcm_event **e;

    HEIMDAL_MUTEX_lock(&events_mutex);

    /* Only run event queue every N seconds */
    if (now < last_run + KCM_EVENT_QUEUE_INTERVAL) {
	HEIMDAL_MUTEX_unlock(&events_mutex);
	return 0;
    }

    /* go through events list, fire and expire */
    for (e = &events_head; *e != NULL; e = &(*e)->next) {
	if ((*e)->valid == 0)
	    continue;

	if (now >= (*e)->fire_time) {
	    ret = kcm_fire_event(context, e);
	    if (ret) {
		kcm_log(1, "Could not fire event for cache %s: %s",
			(*e)->ccache->name, krb5_get_err_text(context, ret));
	    }
	} else if ((*e)->expire_time && now >= (*e)->expire_time) {
	    ret = kcm_remove_event_internal(context, e);
	    if (ret) {
		kcm_log(1, "Could not expire event for cache %s: %s",
			(*e)->ccache->name, krb5_get_err_text(context, ret));
	    }
	}

	if (*e == NULL)
	    break;
    }

    last_run = now;

    HEIMDAL_MUTEX_unlock(&events_mutex);

    return 0;
}
Beispiel #6
0
krb5_error_code kcm_debug_ccache(krb5_context context)
{
    kcm_ccache p;

    for (p = ccache_head; p != NULL; p = p->next) {
	char *cpn = NULL, *spn = NULL;
	int ncreds = 0;
	struct kcm_creds *k;

	if ((p->flags & KCM_FLAGS_VALID) == 0) {
	    kcm_log(7, "cache %08x: empty slot");
	    continue;
	}

	KCM_ASSERT_VALID(p);

	for (k = p->creds; k != NULL; k = k->next)
	    ncreds++;

	if (p->client != NULL)
	    krb5_unparse_name(context, p->client, &cpn);
	if (p->server != NULL)
	    krb5_unparse_name(context, p->server, &spn);
	
	kcm_log(7, "cache %08x: name %s refcnt %d flags %04x mode %04o "
		"uid %d gid %d client %s server %s ncreds %d",
		p, p->name, p->refcnt, p->flags, p->mode, p->uid, p->gid,
		(cpn == NULL) ? "<none>" : cpn,
		(spn == NULL) ? "<none>" : spn,
		ncreds);

	if (cpn != NULL)
	    free(cpn);
	if (spn != NULL)
	    free(spn);
    }

    return 0;
}
Beispiel #7
0
static void
log_event(kcm_event *event, char *msg)
{
    char fire_time[64], expire_time[64];

    print_times(event->fire_time, fire_time);
    print_times(event->expire_time, expire_time);

    kcm_log(7, "%s event %08x: fire_time %s fire_count %d expire_time %s "
	    "backoff_time %d action %s cache %s",
	    msg, event, fire_time, event->fire_count, expire_time,
	    event->backoff_time, action_strings[event->action],
	    event->ccache->name);
}
Beispiel #8
0
krb5_error_code
kcm_ccache_destroy_client(krb5_context context,
			  kcm_client *client,
			  const char *name)
{
    krb5_error_code ret;
    kcm_ccache ccache;

    ret = kcm_ccache_resolve_by_name(context, name, &ccache);
    if (ret) {
	kcm_log(1, "Failed to resolve cache %s", name);
	return ret;
    }

    ret = kcm_access(context, client, KCM_OP_DESTROY, ccache);
    kcm_release_ccache(context, ccache);
    if (ret)
	return ret;

    return kcm_ccache_destroy(context, name);
}
Beispiel #9
0
/*
 * Setup default events for a new credential
 */
static krb5_error_code
kcm_ccache_make_default_event(krb5_context context,
			      kcm_event *event,
			      krb5_creds *newcred)
{
    krb5_error_code ret = 0;
    kcm_ccache ccache = event->ccache;

    event->fire_time = 0; 
    event->expire_time = 0;
    event->backoff_time = KCM_EVENT_DEFAULT_BACKOFF_TIME;

    if (newcred == NULL) {
	/* no creds, must be acquire creds request */
	if ((ccache->flags & KCM_MASK_KEY_PRESENT) == 0) {
	    kcm_log(0, "Cannot acquire credentials without a key");
	    return KRB5_FCC_INTERNAL;
	}

	event->fire_time = time(NULL); /* right away */
	event->action = KCM_EVENT_ACQUIRE_CREDS;
    } else if (is_primary_credential_p(context, ccache, newcred)) {
	if (newcred->flags.b.renewable) {
	    event->action = KCM_EVENT_RENEW_CREDS;
	    ccache->flags |= KCM_FLAGS_RENEWABLE;
	} else {
	    if (ccache->flags & KCM_MASK_KEY_PRESENT)
		event->action = KCM_EVENT_ACQUIRE_CREDS;
	    else
		event->action = KCM_EVENT_NONE;
	    ccache->flags &= ~(KCM_FLAGS_RENEWABLE);
	}
	/* requeue with some slop factor */
	event->fire_time = newcred->times.endtime - KCM_EVENT_QUEUE_INTERVAL;
    } else {
	event->action = KCM_EVENT_NONE;
    }

    return ret;
}
Beispiel #10
0
krb5_error_code
kcm_ccache_resolve_client(krb5_context context,
			  kcm_client *client,
			  kcm_operation opcode,
			  const char *name,
			  kcm_ccache *ccache)
{
    krb5_error_code ret;

    ret = kcm_ccache_resolve_by_name(context, name, ccache);
    if (ret) {
	kcm_log(1, "Failed to resolve cache %s", name);
	return ret;
    }

    ret = kcm_access(context, client, opcode, *ccache);
    if (ret) {
	ret = KRB5_FCC_NOFILE; /* don't disclose */
	kcm_release_ccache(context, *ccache);
    }

    return ret;
}
Beispiel #11
0
static krb5_error_code
change_pw_and_update_keytab(krb5_context context,
			    kcm_ccache ccache)
{
    char newpw[121];
    krb5_error_code ret;
    unsigned kvno;
    krb5_salt salt;
    krb5_enctype *etypes = NULL;
    int i;
    char *cpn = NULL;
    char **spns = NULL;

    krb5_data_zero(&salt.saltvalue);

    ret = krb5_unparse_name(context, ccache->client, &cpn);
    if (ret) {
	kcm_log(0, "Failed to unparse name: %s",
		krb5_get_err_text(context, ret));
	goto out;
    }

    ret = krb5_get_default_in_tkt_etypes(context, &etypes);
    if (ret) {
	kcm_log(0, "Failed to determine default encryption types: %s",
		krb5_get_err_text(context, ret));
	goto out;
    }

    /* Generate a random password (there is no set keys protocol) */
    generate_random_pw(context, newpw, sizeof(newpw));

    /* Change it */
    ret = change_pw(context, ccache, cpn, newpw);
    if (ret)
	goto out;

    /* Do an AS-REQ to determine salt and key version number */
    ret = get_salt_and_kvno(context, ccache, etypes, cpn, newpw,
			    &salt, &kvno);
    if (ret) {
	kcm_log(0, "Failed to determine salting principal for principal %s: %s",
		cpn, krb5_get_err_text(context, ret));
	goto out;
    }

    /* Add canonical name */
    ret = update_keytab_entries(context, ccache, etypes, cpn,
				NULL, newpw, salt, kvno);
    if (ret)
	goto out;

    /* Add SPN aliases, if any */
    spns = krb5_config_get_strings(context, NULL, "kcm",
				   "system_ccache", "spn_aliases", NULL);
    if (spns != NULL) {
	for (i = 0; spns[i] != NULL; i++) {
	    ret = update_keytab_entries(context, ccache, etypes, cpn,
					spns[i], newpw, salt, kvno);
	    if (ret)
		goto out;
	}
    }

    kcm_log(0, "Changed expired password for principal %s in cache %s",
	    cpn, ccache->name);

out:
    if (cpn != NULL)
	free(cpn);
    if (spns != NULL)
	krb5_config_free_strings(spns);
    if (etypes != NULL)
	free(etypes);
    krb5_free_salt(context, salt);
    memset(newpw, 0, sizeof(newpw));

    return ret;
}
Beispiel #12
0
krb5_error_code
kcm_ccache_new_client(krb5_context context,
		      kcm_client *client,
		      const char *name,
		      kcm_ccache *ccache_p)
{
    krb5_error_code ret;
    kcm_ccache ccache;

    ret = kcm_ccache_resolve_by_name(context, name, &ccache);
    if (ret == 0) {
	if ((ccache->uid != client->uid) && !CLIENT_IS_ROOT(client))
	    return KRB5_FCC_PERM;
    } else if (ret != KRB5_FCC_NOFILE && !(CLIENT_IS_ROOT(client) && ret == KRB5_FCC_PERM)) {
		return ret;
    }

    if (ret == KRB5_FCC_NOFILE) {
	ret = kcm_ccache_new(context, name, &ccache);
	if (ret) {
	    kcm_log(1, "Failed to initialize cache %s", name);
	    return ret;
	}

	/* bind to current client */
	ccache->uid = client->uid;
	ccache->session = client->session;

	/* 
	 * add notification when the session goes away, so we can
	 * remove the credential
	 */
	kcm_session_add(client->session);

    } else {
	ret = kcm_zero_ccache_data(context, ccache);
	if (ret) {
	    kcm_log(1, "Failed to empty cache %s", name);
	    kcm_release_ccache(context, ccache);
	    return ret;
	}
	heim_ipc_event_cancel(ccache->renew_event);
	heim_ipc_event_cancel(ccache->expire_event);
    }

    ret = kcm_access(context, client, KCM_OP_INITIALIZE, ccache);
    if (ret) {
	kcm_release_ccache(context, ccache);
	kcm_ccache_destroy(context, name);
	return ret;
    }

    /*
     * Finally, if the user is root and the cache was created under
     * another user's name, chown the cache to that user.
     */
    if (CLIENT_IS_ROOT(client)) {
	unsigned long uid;
	int matches = sscanf(name,"%ld:",&uid);
	if (matches == 0)
	    matches = sscanf(name,"%ld",&uid);
	if (matches == 1) {
	    kcm_chown(context, client, ccache, (uid_t)uid);
	}
    }

    *ccache_p = ccache;
    return 0;
}
Beispiel #13
0
krb5_error_code
kcm_ccache_acquire(krb5_context context,
		   kcm_ccache ccache,
		   krb5_creds **credp)
{
    krb5_error_code ret = 0;
    krb5_creds cred;
    krb5_const_realm realm;
    krb5_get_init_creds_opt opt;
    krb5_ccache_data ccdata;
    char *in_tkt_service = NULL;
    int done = 0;

    memset(&cred, 0, sizeof(cred));

    KCM_ASSERT_VALID(ccache);

    /* We need a cached key or keytab to acquire credentials */
    if (ccache->flags & KCM_FLAGS_USE_CACHED_KEY) {
	if (ccache->key.keyblock.keyvalue.length == 0)
	    krb5_abortx(context,
			"kcm_ccache_acquire: KCM_FLAGS_USE_CACHED_KEY without key");
    } else if (ccache->flags & KCM_FLAGS_USE_KEYTAB) {
	if (ccache->key.keytab == NULL)
	    krb5_abortx(context,
			"kcm_ccache_acquire: KCM_FLAGS_USE_KEYTAB without keytab");
    } else {
	kcm_log(0, "Cannot acquire initial credentials for cache %s without key",
		ccache->name);
	return KRB5_FCC_INTERNAL;
    }
	
    HEIMDAL_MUTEX_lock(&ccache->mutex);

    /* Fake up an internal ccache */
    kcm_internal_ccache(context, ccache, &ccdata);

    /* Now, actually acquire the creds */
    if (ccache->server != NULL) {
	ret = krb5_unparse_name(context, ccache->server, &in_tkt_service);
	if (ret) {
	    kcm_log(0, "Failed to unparse service principal name for cache %s: %s",
		    ccache->name, krb5_get_err_text(context, ret));
	    return ret;
	}
    }

    realm = krb5_principal_get_realm(context, ccache->client);

    krb5_get_init_creds_opt_init(&opt);
    krb5_get_init_creds_opt_set_default_flags(context, "kcm", realm, &opt);
    if (ccache->tkt_life != 0)
	krb5_get_init_creds_opt_set_tkt_life(&opt, ccache->tkt_life);
    if (ccache->renew_life != 0)
	krb5_get_init_creds_opt_set_renew_life(&opt, ccache->renew_life);

    if (ccache->flags & KCM_FLAGS_USE_CACHED_KEY) {
	ret = krb5_get_init_creds_keyblock(context,
					   &cred,
					   ccache->client,
					   &ccache->key.keyblock,
					   0,
					   in_tkt_service,
					   &opt);
    } else {
	/* loosely based on lib/krb5/init_creds_pw.c */
	while (!done) {
	    ret = krb5_get_init_creds_keytab(context,
					     &cred,
					     ccache->client,
					     ccache->key.keytab,
					     0,
					     in_tkt_service,
					     &opt);
	    switch (ret) {
	    case KRB5KDC_ERR_KEY_EXPIRED:
		if (in_tkt_service != NULL &&
		    strcmp(in_tkt_service, "kadmin/changepw") == 0) {
		    goto out;
		}

		ret = change_pw_and_update_keytab(context, ccache);
		if (ret)
		    goto out;
		break;
	    case 0:
	    default:
		done = 1;
		break;
	    }
	}
    }

    if (ret) {
	kcm_log(0, "Failed to acquire credentials for cache %s: %s",
		ccache->name, krb5_get_err_text(context, ret));
	if (in_tkt_service != NULL)
	    free(in_tkt_service);
	goto out;
    }

    if (in_tkt_service != NULL)
	free(in_tkt_service);

    /* Swap them in */
    kcm_ccache_remove_creds_internal(context, ccache);

    ret = kcm_ccache_store_cred_internal(context, ccache, &cred, 0, credp);
    if (ret) {
	kcm_log(0, "Failed to store credentials for cache %s: %s",
		ccache->name, krb5_get_err_text(context, ret));
	krb5_free_cred_contents(context, &cred);
	goto out;
    }

out:
    HEIMDAL_MUTEX_unlock(&ccache->mutex);

    return ret;
}
Beispiel #14
0
static krb5_error_code
change_pw(krb5_context context,
	  kcm_ccache ccache,
	  char *cpn,
	  char *newpw)
{
    krb5_error_code ret;
    krb5_creds cpw_cred;
    int result_code;
    krb5_data result_code_string;
    krb5_data result_string;
    krb5_get_init_creds_opt options;

    memset(&cpw_cred, 0, sizeof(cpw_cred));

    krb5_get_init_creds_opt_init(&options);
    krb5_get_init_creds_opt_set_tkt_life(&options, 60);
    krb5_get_init_creds_opt_set_forwardable(&options, FALSE);
    krb5_get_init_creds_opt_set_proxiable(&options, FALSE);

    krb5_data_zero(&result_code_string);
    krb5_data_zero(&result_string);

    ret = krb5_get_init_creds_keytab(context,
				     &cpw_cred,
				     ccache->client,
				     ccache->key.keytab,
				     0,
				     "kadmin/changepw",
				     &options);
    if (ret) {
	kcm_log(0, "Failed to acquire password change credentials "
		"for principal %s: %s", 
		cpn, krb5_get_err_text(context, ret));
	goto out;
    }

    ret = krb5_set_password(context,
			    &cpw_cred,
			    newpw,
			    ccache->client,
			    &result_code,
			    &result_code_string,
			    &result_string);
    if (ret) {
	kcm_log(0, "Failed to change password for principal %s: %s",
		cpn, krb5_get_err_text(context, ret));
	goto out;
    }

    if (result_code) {
	kcm_log(0, "Failed to change password for principal %s: %.*s",
		cpn,
		(int)result_string.length,
		result_string.length > 0 ? (char *)result_string.data : "");
	goto out;
    }

out:
    krb5_data_free(&result_string);
    krb5_data_free(&result_code_string);
    krb5_free_cred_contents(context, &cpw_cred);

    return ret;
}
Beispiel #15
0
static krb5_error_code
get_salt_and_kvno(krb5_context context,
		  kcm_ccache ccache,
		  krb5_enctype *etypes,
		  char *cpn,
		  char *newpw,
		  krb5_salt *salt,
		  unsigned *kvno)
{
    krb5_error_code ret;
    krb5_creds creds;
    krb5_ccache_data ccdata;
    krb5_flags options = 0;
    krb5_kdc_rep reply;
    struct kcm_keyseed_data s;

    memset(&creds, 0, sizeof(creds));
    memset(&reply, 0, sizeof(reply));

    s.password = NULL;
    s.salt.salttype = (int)ETYPE_NULL;
    krb5_data_zero(&s.salt.saltvalue);

    *kvno = 0;
    kcm_internal_ccache(context, ccache, &ccdata);
    s.password = newpw;

    /* Do an AS-REQ to determine salt and key version number */
    ret = krb5_copy_principal(context, ccache->client, &creds.client);
    if (ret)
	return ret;

    /* Yes, get a ticket to ourselves */
    ret = krb5_copy_principal(context, ccache->client, &creds.server);
    if (ret) {
	krb5_free_principal(context, creds.client);
	return ret;
    }
	
    ret = krb5_get_in_tkt(context,
			  options,
			  NULL,
			  etypes,
			  NULL,
			  kcm_password_key_proc,
			  &s,
			  NULL,
			  NULL,
			  &creds,
			  &ccdata,
			  &reply);
    if (ret) {
	kcm_log(0, "Failed to get self ticket for principal %s: %s",
		cpn, krb5_get_err_text(context, ret));
	krb5_free_salt(context, s.salt);
    } else {
	*salt = s.salt; /* retrieve stashed salt */
	if (reply.kdc_rep.enc_part.kvno != NULL)
	    *kvno = *(reply.kdc_rep.enc_part.kvno);
    }
    /* ccache may have been modified but it will get trashed anyway */

    krb5_free_cred_contents(context, &creds);
    krb5_free_kdc_rep(context, &reply);

    return ret;
}
Beispiel #16
0
krb5_error_code
kcm_ccache_refresh(krb5_context context,
		   kcm_ccache ccache,
		   krb5_creds **credp)
{
    krb5_error_code ret;
    krb5_creds in, *out;
    krb5_kdc_flags flags;
    krb5_const_realm realm;
    krb5_ccache_data ccdata;
    const char *estr;

    memset(&in, 0, sizeof(in));

    KCM_ASSERT_VALID(ccache);

    if (ccache->client == NULL) {
	/* no primary principal */
	kcm_log(0, "Refresh credentials requested but no client principal");
	return KRB5_CC_NOTFOUND;
    }

    HEIMDAL_MUTEX_lock(&ccache->mutex);

    /* Fake up an internal ccache */
    kcm_internal_ccache(context, ccache, &ccdata);

    /* Find principal */
    in.client = ccache->client;

    if (ccache->server != NULL) {
	ret = krb5_copy_principal(context, ccache->server, &in.server);
	if (ret) {
	    estr = krb5_get_error_message(context, ret);
	    kcm_log(0, "Failed to copy service principal: %s",
		    estr);
	    krb5_free_error_message(context, estr);
	    goto out;
	}
    } else {
	realm = krb5_principal_get_realm(context, in.client);
	ret = krb5_make_principal(context, &in.server, realm,
				  KRB5_TGS_NAME, realm, NULL);
	if (ret) {
	    estr = krb5_get_error_message(context, ret);
	    kcm_log(0, "Failed to make TGS principal for realm %s: %s",
		    realm, estr);
	    krb5_free_error_message(context, estr);
	    goto out;
	}
    }

    if (ccache->tkt_life)
	in.times.endtime = time(NULL) + ccache->tkt_life;
    if (ccache->renew_life)
	in.times.renew_till = time(NULL) + ccache->renew_life;

    flags.i = 0;
    flags.b.renewable = TRUE;
    flags.b.renew = TRUE;

    ret = krb5_get_kdc_cred(context,
			    &ccdata,
			    flags,
			    NULL,
			    NULL,
			    &in,
			    &out);
    if (ret) {
	estr = krb5_get_error_message(context, ret);
	kcm_log(0, "Failed to renew credentials for cache %s: %s",
		ccache->name, estr);
	krb5_free_error_message(context, estr);
	goto out;
    }

    /* Swap them in */
    kcm_ccache_remove_creds_internal(context, ccache);

    ret = kcm_ccache_store_cred_internal(context, ccache, out, 0, credp);
    if (ret) {
	estr = krb5_get_error_message(context, ret);
	kcm_log(0, "Failed to store credentials for cache %s: %s",
		ccache->name, estr);
	krb5_free_error_message(context, estr);
	krb5_free_creds(context, out);
	goto out;
    }

    free(out); /* but not contents */

out:
    HEIMDAL_MUTEX_unlock(&ccache->mutex);

    return ret;
}
Beispiel #17
0
void
kcm_session_add(pid_t session_id)
{
    kcm_log(1, "monitor session: %d\n", session_id);
}
Beispiel #18
0
krb5_error_code
kcm_access(krb5_context context,
	   kcm_client *client,
	   kcm_operation opcode,
	   kcm_ccache ccache)
{
    int read_p = 0;
    int write_p = 0;
    uint16_t mask;
    krb5_error_code ret;

    KCM_ASSERT_VALID(ccache);

    switch (opcode) {
    case KCM_OP_INITIALIZE:
    case KCM_OP_DESTROY:
    case KCM_OP_STORE:
    case KCM_OP_REMOVE_CRED:
    case KCM_OP_SET_FLAGS:
    case KCM_OP_CHOWN:
    case KCM_OP_CHMOD:
    case KCM_OP_GET_INITIAL_TICKET:
    case KCM_OP_GET_TICKET:
	write_p = 1;
	read_p = 0;
	break;
    case KCM_OP_NOOP:
    case KCM_OP_GET_NAME:
    case KCM_OP_RESOLVE:
    case KCM_OP_GEN_NEW:
    case KCM_OP_RETRIEVE:
    case KCM_OP_GET_PRINCIPAL:
    case KCM_OP_GET_FIRST:
    case KCM_OP_GET_NEXT:
    case KCM_OP_END_GET:
    case KCM_OP_MAX:
	write_p = 0;
	read_p = 1;
	break;
    }

    if (ccache->flags & KCM_FLAGS_OWNER_IS_SYSTEM) {
	/* System caches cannot be reinitialized or destroyed by users */
	if (opcode == KCM_OP_INITIALIZE ||
	    opcode == KCM_OP_DESTROY ||
	    opcode == KCM_OP_REMOVE_CRED) {
	    ret = KRB5_FCC_PERM;
	    goto out;
	}

	/* Let root always read system caches */
	if (client->uid == 0) {
	    ret = 0;
	    goto out;
	}
    }

    mask = 0;

    /* Root may do whatever they like */
    if (client->uid == ccache->uid || CLIENT_IS_ROOT(client)) {
	if (read_p)
	    mask |= S_IRUSR;
	if (write_p)
	    mask |= S_IWUSR;
    } else if (client->gid == ccache->gid || CLIENT_IS_ROOT(client)) {
	if (read_p)
	    mask |= S_IRGRP;
	if (write_p)
	    mask |= S_IWGRP;
    } else {
	if (read_p)
	    mask |= S_IROTH;
	if (write_p)
	    mask |= S_IWOTH;
    }

    ret = ((ccache->mode & mask) == mask) ? 0 : KRB5_FCC_PERM;

out:
    if (ret) {
	kcm_log(2, "Process %d is not permitted to call %s on cache %s",
		client->pid, kcm_op2string(opcode), ccache->name);
    }

    return ret;
}
Beispiel #19
0
krb5_error_code
kcm_access(krb5_context context,
	   kcm_client *client,
	   kcm_operation opcode,
	   kcm_ccache ccache)
{
    int read_p = 0;
    int write_p = 0;
    uint16_t mask;
    krb5_error_code ret;

    KCM_ASSERT_VALID(ccache);

    switch (opcode) {
    case KCM_OP_INITIALIZE:
    case KCM_OP_DESTROY:
    case KCM_OP_STORE:
    case KCM_OP_REMOVE_CRED:
    case KCM_OP_SET_FLAGS:
    case KCM_OP_CHOWN:
    case KCM_OP_CHMOD:
    case KCM_OP_GET_INITIAL_TICKET:
    case KCM_OP_GET_TICKET:
    case KCM_OP_MOVE_CACHE:
    case KCM_OP_SET_DEFAULT_CACHE:
    case KCM_OP_SET_KDC_OFFSET:
	write_p = 1;
	read_p = 0;
	break;
    case KCM_OP_NOOP:
    case KCM_OP_GET_NAME:
    case KCM_OP_RESOLVE:
    case KCM_OP_GEN_NEW:
    case KCM_OP_RETRIEVE:
    case KCM_OP_GET_PRINCIPAL:
    case KCM_OP_GET_CRED_UUID_LIST:
    case KCM_OP_GET_CRED_BY_UUID:
    case KCM_OP_GET_CACHE_UUID_LIST:
    case KCM_OP_GET_CACHE_BY_UUID:
    case KCM_OP_GET_DEFAULT_CACHE:
    case KCM_OP_GET_KDC_OFFSET:
	write_p = 0;
	read_p = 1;
	break;
    default:
	ret = KRB5_FCC_PERM;
	goto out;
    }

    if (ccache->flags & KCM_FLAGS_OWNER_IS_SYSTEM) {
	/* System caches cannot be reinitialized or destroyed by users */
	if (opcode == KCM_OP_INITIALIZE ||
	    opcode == KCM_OP_DESTROY ||
	    opcode == KCM_OP_REMOVE_CRED ||
	    opcode == KCM_OP_MOVE_CACHE) {
	    ret = KRB5_FCC_PERM;
	    goto out;
	}

	/* Let root always read system caches */
	if (CLIENT_IS_ROOT(client)) {
	    ret = 0;
	    goto out;
	}
    }

    /* start out with "other" mask */
    mask = S_IROTH|S_IWOTH;

    /* root can do anything */
    if (CLIENT_IS_ROOT(client)) {
	if (read_p)
	    mask |= S_IRUSR|S_IRGRP|S_IROTH;
	if (write_p)
	    mask |= S_IWUSR|S_IWGRP|S_IWOTH;
    }
    /* same session same as owner */
    if (kcm_is_same_session(client, ccache->uid, ccache->session)) {
	if (read_p)
	    mask |= S_IROTH;
	if (write_p)
	    mask |= S_IWOTH;
    }
    /* owner */
    if (client->uid == ccache->uid) {
	if (read_p)
	    mask |= S_IRUSR;
	if (write_p)
	    mask |= S_IWUSR;
    }
    /* group */
    if (client->gid == ccache->gid) {
	if (read_p)
	    mask |= S_IRGRP;
	if (write_p)
	    mask |= S_IWGRP;
    }

    ret = (ccache->mode & mask) ? 0 : KRB5_FCC_PERM;

out:
    if (ret) {
	kcm_log(2, "Process %d is not permitted to call %s on cache %s",
		client->pid, kcm_op2string(opcode), ccache->name);
    }

    return ret;
}
Beispiel #20
0
krb5_error_code
kcm_ccache_acquire(krb5_context context,
		   kcm_ccache ccache,
		   time_t *expire)
{
    krb5_error_code ret = 0;
    krb5_creds cred;
    krb5_const_realm realm;
    krb5_get_init_creds_opt *opt = NULL;
    krb5_ccache_data ccdata;
    char *in_tkt_service = NULL;

    *expire = 0;
    memset(&cred, 0, sizeof(cred));

    KCM_ASSERT_VALID(ccache);

    /* We need a cached key or keytab to acquire credentials */
    if (ccache->flags & KCM_FLAGS_USE_PASSWORD) {
	if (ccache->password == NULL)
	    krb5_abortx(context,
			"kcm_ccache_acquire: KCM_FLAGS_USE_PASSWORD without password");
    } else if (ccache->flags & KCM_FLAGS_USE_KEYTAB) {
	if (ccache->keytab == NULL)
	    krb5_abortx(context,
			"kcm_ccache_acquire: KCM_FLAGS_USE_KEYTAB without keytab");
    } else {
	kcm_log(0, "Cannot acquire initial credentials for cache %s without key",
		ccache->name);
	return KRB5_FCC_INTERNAL;
    }

    /* Fake up an internal ccache */
    kcm_internal_ccache(context, ccache, &ccdata);

    /* Now, actually acquire the creds */
    if (ccache->server != NULL) {
	ret = krb5_unparse_name(context, ccache->server, &in_tkt_service);
	if (ret) {
	    kcm_log(0, "Failed to unparse service name for cache %s",
		    ccache->name);
	    return ret;
	}
    }

    realm = krb5_principal_get_realm(context, ccache->client);

    ret = krb5_get_init_creds_opt_alloc(context, &opt);
    if (ret)
	goto out;
    krb5_get_init_creds_opt_set_default_flags(context, "kcm", realm, opt);
    if (ccache->tkt_life != 0)
	krb5_get_init_creds_opt_set_tkt_life(opt, ccache->tkt_life);
    if (ccache->renew_life != 0)
	krb5_get_init_creds_opt_set_renew_life(opt, ccache->renew_life);

    krb5_get_init_creds_opt_set_forwardable(opt, 1);

    if (ccache->flags & KCM_FLAGS_USE_PASSWORD) {
	ret = krb5_get_init_creds_password(context,
					   &cred,
					   ccache->client,
					   ccache->password,
					   NULL,
					   NULL,
					   0,
					   in_tkt_service,
					   opt);
    } else {
	ret = krb5_get_init_creds_keytab(context,
					 &cred,
					 ccache->client,
					 ccache->keytab,
					 0,
					 in_tkt_service,
					 opt);
    }

    if (ret) {
	const char *msg = krb5_get_error_message(context, ret);
	kcm_log(0, "Failed to acquire credentials for cache %s: %s",
		ccache->name, msg);
	krb5_free_error_message(context, msg);
	if (in_tkt_service != NULL)
	    free(in_tkt_service);
	goto out;
    }

    if (in_tkt_service != NULL)
	free(in_tkt_service);

    /* Swap them in */
    kcm_ccache_remove_creds_internal(context, ccache);

    ret = kcm_ccache_store_cred_internal(context, ccache, &cred, NULL, 0);
    if (ret) {
	const char *msg = krb5_get_error_message(context, ret);
	kcm_log(0, "Failed to store credentials for cache %s: %s",
		ccache->name, msg);
	krb5_free_error_message(context, msg);
	krb5_free_cred_contents(context, &cred);
	goto out;
    }

    *expire = cred.times.endtime;

out:
    if (opt)
	krb5_get_init_creds_opt_free(context, opt);

    return ret;
}
Beispiel #21
0
krb5_error_code
kcm_ccache_new_client(krb5_context context,
		      kcm_client *client,
		      const char *name,
		      kcm_ccache *ccache_p)
{
    krb5_error_code ret;
    kcm_ccache ccache;

    /* We insist the ccache name starts with UID or UID: */
    if (name_constraints != 0) {
	char prefix[64];
	size_t prefix_len;
	int bad = 1;

	snprintf(prefix, sizeof(prefix), "%ld:", (long)client->uid);
	prefix_len = strlen(prefix);

	if (strncmp(name, prefix, prefix_len) == 0)
	    bad = 0;
	else {
	    prefix[prefix_len - 1] = '\0';
	    if (strcmp(name, prefix) == 0)
		bad = 0;
	}

	/* Allow root to create badly-named ccaches */
	if (bad && !CLIENT_IS_ROOT(client))
	    return KRB5_CC_BADNAME;
    }

    ret = kcm_ccache_resolve(context, name, &ccache);
    if (ret == 0) {
	if ((ccache->uid != client->uid ||
	     ccache->gid != client->gid) && !CLIENT_IS_ROOT(client))
	    return KRB5_FCC_PERM;
    } else if (ret != KRB5_FCC_NOFILE && !(CLIENT_IS_ROOT(client) && ret == KRB5_FCC_PERM)) {
		return ret;
    }

    if (ret == KRB5_FCC_NOFILE) {
	ret = kcm_ccache_new(context, name, &ccache);
	if (ret) {
	    kcm_log(1, "Failed to initialize cache %s: %s",
		    name, krb5_get_err_text(context, ret));
	    return ret;
	}

	/* bind to current client */
	ccache->uid = client->uid;
	ccache->gid = client->gid;
	ccache->session = client->session;
    } else {
	ret = kcm_zero_ccache_data(context, ccache);
	if (ret) {
	    kcm_log(1, "Failed to empty cache %s: %s",
		    name, krb5_get_err_text(context, ret));
	    kcm_release_ccache(context, ccache);
	    return ret;
	}
	kcm_cleanup_events(context, ccache);
    }

    ret = kcm_access(context, client, KCM_OP_INITIALIZE, ccache);
    if (ret) {
	kcm_release_ccache(context, ccache);
	kcm_ccache_destroy(context, name);
	return ret;
    }

    /*
     * Finally, if the user is root and the cache was created under
     * another user's name, chown the cache to that user and their
     * default gid.
     */
    if (CLIENT_IS_ROOT(client)) {
	unsigned long uid;
	int matches = sscanf(name,"%ld:",&uid);
	if (matches == 0)
	    matches = sscanf(name,"%ld",&uid);
	if (matches == 1) {
	    struct passwd *pwd = getpwuid(uid);
	    if (pwd != NULL) {
		gid_t gid = pwd->pw_gid;
		kcm_chown(context, client, ccache, uid, gid);
	    }
	}
    }

    *ccache_p = ccache;
    return 0;
}
Beispiel #22
0
static krb5_error_code
kcm_fire_event(krb5_context context,
	       kcm_event **e)
{
    kcm_event *event;
    krb5_error_code ret;
    krb5_creds *credp = NULL;
    int oneshot = 1;

    event = *e;

    switch (event->action) {
    case KCM_EVENT_ACQUIRE_CREDS:
	ret = kcm_ccache_acquire(context, event->ccache, &credp);
	oneshot = 0;
	break;
    case KCM_EVENT_RENEW_CREDS:
	ret = kcm_ccache_refresh(context, event->ccache, &credp);
	if (ret == KRB5KRB_AP_ERR_TKT_EXPIRED) {
	    ret = kcm_ccache_acquire(context, event->ccache, &credp);
	}
	oneshot = 0;
	break;
    case KCM_EVENT_DESTROY_CREDS:
	ret = kcm_ccache_destroy(context, event->ccache->name);
	break;
    case KCM_EVENT_DESTROY_EMPTY_CACHE:
	ret = kcm_ccache_destroy_if_empty(context, event->ccache);
	break;
    default:
	ret = KRB5_FCC_INTERNAL;
	break;
    }

    event->fire_count++;

    if (ret) {
	/* Reschedule failed event for another time */ 
	event->fire_time += event->backoff_time;
	if (event->backoff_time < KCM_EVENT_MAX_BACKOFF_TIME)
	    event->backoff_time *= 2;

	/* Remove it if it would never get executed */
	if (event->expire_time &&
	    event->fire_time > event->expire_time)
	    kcm_remove_event_internal(context, e);
    } else {
	if (!oneshot) {
	    char *cpn;

	    if (krb5_unparse_name(context, event->ccache->client,
				  &cpn))
		cpn = NULL;

	    kcm_log(0, "%s credentials in cache %s for principal %s",
		    (event->action == KCM_EVENT_ACQUIRE_CREDS) ?
			"Acquired" : "Renewed",
		    event->ccache->name,
		    (cpn != NULL) ? cpn : "<none>");

	    if (cpn != NULL)
		free(cpn);

	    /* Succeeded, but possibly replaced with another event */
	    ret = kcm_ccache_make_default_event(context, event, credp);
	    if (ret || event->action == KCM_EVENT_NONE)
		oneshot = 1;
	    else
		log_event(event, "requeuing");
	}
	if (oneshot)
	    kcm_remove_event_internal(context, e);
    }

    return ret;
}