/* realmd -- Realm configuration service
*
* Copyright 2012 Red Hat Inc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: Stef Walter <stefw@gnome.org>
*/
#include "config.h"
#include "realm-command.h"
#include "realm-credential.h"
#include "realm-daemon.h"
#include "realm-dbus-constants.h"
#include "realm-dbus-generated.h"
#include "realm-diagnostics.h"
#include "realm-disco.h"
#include "realm-errors.h"
#include "realm-invocation.h"
#include "realm-kerberos.h"
#include "realm-kerberos-membership.h"
#include "realm-login-name.h"
#include "realm-options.h"
#include "realm-packages.h"
#include "realm-provider.h"
#include "realm-settings.h"
#include <krb5/krb5.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
struct _RealmKerberosPrivate {
RealmDisco *disco;
RealmDbusRealm *realm_iface;
RealmDbusKerberos *kerberos_iface;
RealmDbusKerberosMembership *membership_iface;
};
enum {
PROP_0,
PROP_NAME,
PROP_DISCO,
PROP_PROVIDER,
PROP_MANAGES_SYSTEM,
};
/* A global weak pointer which tracks the manage-system domain */
static RealmKerberos *realm_which_manages_system = NULL;
G_DEFINE_TYPE (RealmKerberos, realm_kerberos, G_TYPE_DBUS_OBJECT_SKELETON);
#define return_if_krb5_failed(ctx, code) G_STMT_START \
if G_LIKELY ((code) == 0) { } else { \
g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
krb5_get_error_message ((ctx), (code))); \
return; \
} G_STMT_END
#define return_val_if_krb5_failed(ctx, code, val) G_STMT_START \
if G_LIKELY ((code) == 0) { } else { \
g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
krb5_get_error_message ((ctx), (code))); \
return (val); \
} G_STMT_END
#define warn_if_krb5_failed(ctx, code) G_STMT_START \
if G_LIKELY ((code) == 0) { } else { \
g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
krb5_get_error_message ((ctx), (code))); \
} G_STMT_END
typedef struct {
RealmKerberos *self;
GDBusMethodInvocation *invocation;
RealmCredential *cred;
} MethodClosure;
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif
static MethodClosure *
method_closure_new (RealmKerberos *self,
GDBusMethodInvocation *invocation)
{
MethodClosure *method = g_new0 (MethodClosure, 1);
method->self = g_object_ref (self);
method->invocation = g_object_ref (invocation);
return method;
}
static void
method_closure_free (MethodClosure *closure)
{
g_object_unref (closure->self);
g_object_unref (closure->invocation);
if (closure->cred)
realm_credential_unref (closure->cred);
g_free (closure);
}
static void
enroll_method_reply (GDBusMethodInvocation *invocation,
GError *error)
{
if (error == NULL) {
realm_diagnostics_info (invocation, "Successfully enrolled machine in realm");
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
} else if (error->domain == REALM_ERROR || error->domain == G_DBUS_ERROR) {
realm_diagnostics_error (invocation, error, NULL);
g_dbus_method_invocation_return_gerror (invocation, error);
} else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
realm_diagnostics_error (invocation, error, "Cancelled");
g_dbus_method_invocation_return_error (invocation, REALM_ERROR, REALM_ERROR_CANCELLED,
_("Operation was cancelled."));
} else {
realm_diagnostics_error (invocation, error, "Failed to enroll machine in realm");
g_dbus_method_invocation_return_error (invocation, REALM_ERROR, REALM_ERROR_FAILED,
_("Failed to enroll machine in realm. See diagnostics."));
}
realm_invocation_unlock_daemon (invocation);
}
static void
on_name_caches_flush (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
MethodClosure *closure = user_data;
GError *error = NULL;
gint status;
status = realm_command_run_finish (result, NULL, &error);
if (status != 0) {
realm_diagnostics_error (closure->invocation, error,
"Flushing name caches failed");
}
g_clear_error (&error);
enroll_method_reply (closure->invocation, NULL);
method_closure_free (closure);
}
static void
on_enroll_complete (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
MethodClosure *closure = user_data;
RealmKerberosMembershipIface *iface;
GCancellable *cancellable;
GError *error = NULL;
iface = REALM_KERBEROS_MEMBERSHIP_GET_IFACE (closure->self);
g_return_if_fail (iface->join_finish != NULL);
cancellable = realm_invocation_get_cancellable (closure->invocation);
if (!g_cancellable_set_error_if_cancelled (cancellable, &error))
(iface->join_finish) (REALM_KERBEROS_MEMBERSHIP (closure->self), result, &error);
if (error != NULL) {
enroll_method_reply (closure->invocation, error);
method_closure_free (closure);
g_clear_error (&error);
/* Only flush the name caches if not in install mode */
} else if (!realm_daemon_is_install_mode ()) {
realm_command_run_known_async ("name-caches-flush", NULL, closure->invocation,
on_name_caches_flush, closure);
} else {
enroll_method_reply (closure->invocation, NULL);
}
}
static void
unenroll_method_reply (GDBusMethodInvocation *invocation,
GError *error)
{
if (error == NULL) {
realm_diagnostics_info (invocation, "Successfully unenrolled machine from realm");
g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
} else if (error->domain == REALM_ERROR || error->domain == G_DBUS_ERROR) {
realm_diagnostics_error (invocation, error, NULL);
g_dbus_method_invocation_return_gerror (invocation, error);
} else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
realm_diagnostics_error (invocation, error, "Cancelled");
g_dbus_method_invocation_return_error (invocation, REALM_ERROR, REALM_ERROR_CANCELLED,
_("Operation was cancelled."));
} else {
realm_diagnostics_error (invocation, error, "Failed to unenroll machine from realm");
g_dbus_method_invocation_return_error (invocation, REALM_ERROR, REALM_ERROR_FAILED,
_("Failed to unenroll machine from domain. See diagnostics."));
}
realm_invocation_unlock_daemon (invocation);
}
static void
on_unenroll_complete (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
MethodClosure *closure = user_data;
RealmKerberosMembershipIface *iface;
GCancellable *cancellable;
GError *error = NULL;
iface = REALM_KERBEROS_MEMBERSHIP_GET_IFACE (closure->self);
g_return_if_fail (iface->leave_finish != NULL);
cancellable = realm_invocation_get_cancellable (closure->invocation);
if (!g_cancellable_set_error_if_cancelled (cancellable, &error))
(iface->leave_finish) (REALM_KERBEROS_MEMBERSHIP (closure->self), result, &error);
unenroll_method_reply (closure->invocation, error);
g_clear_error (&error);
method_closure_free (closure);
}
static gboolean
is_credential_supported (RealmKerberosMembershipIface *iface,
RealmKerberosMembership *membership,
RealmCredential *cred,
gboolean join,
GError **error)
{
const RealmCredential *supported;
const char *message = NULL;
gboolean found = FALSE;
gint i;
g_assert (iface != NULL);
g_assert (iface->join_creds != NULL);
g_assert (iface->leave_creds != NULL);
supported = (join ? iface->join_creds (membership) : iface->leave_creds (membership));
if (supported) {
for (i = 0; supported[i].type != 0; i++) {
if (cred->type == supported[i].type) {
found = TRUE;
break;
}
}
}
if (found)
return TRUE;
switch (cred->type) {
case REALM_CREDENTIAL_AUTOMATIC:
message = join ? _("Joining this realm without credentials is not supported") :
_("Leaving this realm without credentials is not supported");
break;
case REALM_CREDENTIAL_CCACHE:
message = join ? _("Joining this realm using a credential cache is not supported") :
_("Leaving this realm using a credential cache is not supported");
break;
case REALM_CREDENTIAL_SECRET:
message = join ? _("Joining this realm using a secret is not supported") :
_("Unenrolling this realm using a secret is not supported");
break;
case REALM_CREDENTIAL_PASSWORD:
message = join ? _("Enrolling this realm using a password is not supported") :
_("Unenrolling this realm using a password is not supported");
break;
}
g_set_error_literal (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, message);
return FALSE;
}
static void
join_or_leave (RealmKerberos *self,
GVariant *credential,
GVariant *options,
GDBusMethodInvocation *invocation,
gboolean join)
{
RealmKerberosMembershipIface *iface = REALM_KERBEROS_MEMBERSHIP_GET_IFACE (self);
RealmKerberosMembership *membership = REALM_KERBEROS_MEMBERSHIP (self);
RealmCredential *cred;
MethodClosure *method;
GError *error = NULL;
g_return_if_fail (iface != NULL);
if ((join && iface && iface->join_async == NULL) ||
(!join && iface && iface->leave_async == NULL)) {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED,
join ? _("Joining this realm is not supported") :
_("Leaving this realm is not supported"));
return;
}
cred = realm_credential_parse (credential, &error);
if (error != NULL) {
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
return;
}
if (!is_credential_supported (iface, membership, cred, join, &error)) {
g_dbus_method_invocation_return_gerror (invocation, error);
realm_credential_unref (cred);
g_error_free (error);
return;
}
if (!realm_invocation_lock_daemon (invocation)) {
g_dbus_method_invocation_return_error (invocation, REALM_ERROR, REALM_ERROR_BUSY,
_("Already running another action"));
return;
}
method = method_closure_new (self, invocation);
method->cred = cred;
if (join) {
g_return_if_fail (iface->join_finish != NULL);
(iface->join_async) (membership, cred, options, invocation, on_enroll_complete, method);
} else {
g_return_if_fail (iface->leave_finish != NULL);
(iface->leave_async) (membership, cred, options, invocation, on_unenroll_complete, method);
}
}
static gboolean
handle_join (RealmDbusKerberosMembership *membership,
GDBusMethodInvocation *invocation,
GVariant *credentials,
GVariant *options,
gpointer user_data)
{
RealmKerberos *self = REALM_KERBEROS (user_data);
gchar hostname[HOST_NAME_MAX + 1];
RealmKerberos *manages;
gint ret;
/* Check the host name */
ret = gethostname (hostname, sizeof (hostname));
if (ret < 0 || g_ascii_strcasecmp (hostname, "localhost") == 0 ||
g_ascii_strncasecmp (hostname, "localhost.", 10) == 0 ||
hostname[0] == '.') {
g_dbus_method_invocation_return_error (invocation, REALM_ERROR, REALM_ERROR_BAD_HOSTNAME,
"This computer's host name is not set correctly.");
return TRUE;
}
if (realm_options_manage_system (options, realm_kerberos_get_name (self))) {
manages = realm_kerberos_which_manages_system ();
if (manages != NULL && manages != self) {
g_dbus_method_invocation_return_error (invocation, REALM_ERROR,
REALM_ERROR_ALREADY_CONFIGURED,
_("Already joined to another domain: %s"),
realm_kerberos_get_name (manages));
return TRUE;
}
}
join_or_leave (self, credentials, options, invocation, TRUE);
return TRUE;
}
static gboolean
handle_leave (RealmDbusKerberosMembership *membership,
GDBusMethodInvocation *invocation,
GVariant *credentials,
GVariant *options,
gpointer user_data)
{
RealmKerberos *self = REALM_KERBEROS (user_data);
if (realm_options_computer_ou (options, NULL)) {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"The computer-ou argument is not supported when leaving a domain.");
return TRUE;
}
join_or_leave (self, credentials, options, invocation, FALSE);
return TRUE;
}
static gboolean
handle_deconfigure (RealmDbusRealm *realm,
GDBusMethodInvocation *invocation,
GVariant *options,
gpointer user_data)
{
GVariant *credential;
credential = g_variant_new ("(ss@v)", "automatic", "none",
g_variant_new_variant (g_variant_new_string ("")));
join_or_leave (REALM_KERBEROS (user_data), credential, options, invocation, FALSE);
g_variant_unref (credential);
return TRUE;
}
static void
on_logins_complete (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
MethodClosure *closure = user_data;
RealmKerberosClass *klass;
GError *error = NULL;
klass = REALM_KERBEROS_GET_CLASS (closure->self);
g_return_if_fail (klass->logins_finish != NULL);
if ((klass->logins_finish) (closure->self, result, &error)) {
realm_diagnostics_info (closure->invocation, "Successfully changed permitted logins for realm");
g_dbus_method_invocation_return_value (closure->invocation, g_variant_new ("()"));
} else if (error != NULL &&
(error->domain == REALM_ERROR || error->domain == G_DBUS_ERROR)) {
realm_diagnostics_error (closure->invocation, error, NULL);
g_dbus_method_invocation_return_gerror (closure->invocation, error);
g_error_free (error);
} else {
realm_diagnostics_error (closure->invocation, error, "Failed to change permitted logins");
g_dbus_method_invocation_return_error (closure->invocation, REALM_ERROR, REALM_ERROR_INTERNAL,
_("Failed to change permitted logins. See diagnostics."));
g_error_free (error);
}
realm_invocation_unlock_daemon (closure->invocation);
method_closure_free (closure);
}
static gboolean
handle_change_login_policy (RealmDbusRealm *realm,
GDBusMethodInvocation *invocation,
const gchar *login_policy,
const gchar *const *add,
const gchar *const *remove,
GVariant *options,
gpointer user_data)
{
RealmKerberosLoginPolicy policy = REALM_KERBEROS_POLICY_NOT_SET;
RealmKerberos *self = REALM_KERBEROS (user_data);
RealmKerberosClass *klass;
gchar **policies;
gint policies_set = 0;
gint i;
policies = g_strsplit_set (login_policy, ", \t", -1);
for (i = 0; policies[i] != NULL; i++) {
if (g_str_equal (policies[i], REALM_DBUS_LOGIN_POLICY_ANY)) {
policy = REALM_KERBEROS_ALLOW_ANY_LOGIN;
policies_set++;
} else if (g_str_equal (policies[i], REALM_DBUS_LOGIN_POLICY_REALM)) {
policy = REALM_KERBEROS_ALLOW_REALM_LOGINS;
policies_set++;
} else if (g_str_equal (policies[i], REALM_DBUS_LOGIN_POLICY_PERMITTED)) {
policy = REALM_KERBEROS_ALLOW_PERMITTED_LOGINS;
policies_set++;
} else if (g_str_equal (policies[i], REALM_DBUS_LOGIN_POLICY_DENY)) {
policy = REALM_KERBEROS_DENY_ANY_LOGIN;
policies_set++;
} else {
g_strfreev (policies);
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid or unknown login_policy argument");
return TRUE;
}
}
g_strfreev (policies);
if (policies_set > 1) {
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Conflicting flags in login_policy argument");
return TRUE;
}
if (!realm_invocation_lock_daemon (invocation)) {
g_dbus_method_invocation_return_error (invocation, REALM_ERROR, REALM_ERROR_BUSY,
_("Already running another action"));
return TRUE;
}
klass = REALM_KERBEROS_GET_CLASS (self);
g_return_val_if_fail (klass->logins_async != NULL, FALSE);
(klass->logins_async) (self, invocation, policy, (const gchar **)add,
(const gchar **)remove, options, on_logins_complete,
method_closure_new (self, invocation));
return TRUE;
}
static gboolean
realm_kerberos_authorize_method (GDBusObjectSkeleton *object,
GDBusInterfaceSkeleton *iface,
GDBusMethodInvocation *invocation)
{
return realm_invocation_authorize (invocation);
}
static void
realm_kerberos_init (RealmKerberos *self)
{
GDBusObjectSkeleton *skeleton = G_DBUS_OBJECT_SKELETON (self);
self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, REALM_TYPE_KERBEROS,
RealmKerberosPrivate);
self->pv->realm_iface = realm_dbus_realm_skeleton_new ();
g_signal_connect (self->pv->realm_iface, "handle-deconfigure",
G_CALLBACK (handle_deconfigure), self);
g_signal_connect (self->pv->realm_iface, "handle-change-login-policy",
G_CALLBACK (handle_change_login_policy), self);
g_dbus_object_skeleton_add_interface (skeleton, G_DBUS_INTERFACE_SKELETON (self->pv->realm_iface));
self->pv->kerberos_iface = realm_dbus_kerberos_skeleton_new ();
g_dbus_object_skeleton_add_interface (skeleton, G_DBUS_INTERFACE_SKELETON (self->pv->kerberos_iface));
}
static void
realm_kerberos_constructed (GObject *obj)
{
RealmKerberosMembershipIface *iface;
RealmKerberosMembership *membership;
RealmKerberos *self = REALM_KERBEROS (obj);
const gchar *supported_interfaces[3];
GVariant *supported;
g_return_if_fail (self != NULL);
G_OBJECT_CLASS (realm_kerberos_parent_class)->constructed (obj);
if (REALM_IS_KERBEROS_MEMBERSHIP (self)) {
self->pv->membership_iface = realm_dbus_kerberos_membership_skeleton_new ();
g_signal_connect (self->pv->membership_iface, "handle-join",
G_CALLBACK (handle_join), self);
g_signal_connect (self->pv->membership_iface, "handle-leave",
G_CALLBACK (handle_leave), self);
g_dbus_object_skeleton_add_interface (G_DBUS_OBJECT_SKELETON (self),
G_DBUS_INTERFACE_SKELETON (self->pv->membership_iface));
iface = REALM_KERBEROS_MEMBERSHIP_GET_IFACE (self);
membership = REALM_KERBEROS_MEMBERSHIP (self);
supported = realm_credential_build_supported (iface->join_creds (membership));
realm_dbus_kerberos_membership_set_supported_join_credentials (self->pv->membership_iface, supported);
supported = realm_credential_build_supported (iface->leave_creds (membership));
realm_dbus_kerberos_membership_set_supported_leave_credentials (self->pv->membership_iface, supported);
}
supported_interfaces[0] = REALM_DBUS_KERBEROS_INTERFACE;
if (self->pv->membership_iface)
supported_interfaces[1] = REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE;
else
supported_interfaces[1] = NULL;
supported_interfaces[2] = NULL;
realm_dbus_realm_set_supported_interfaces (self->pv->realm_iface,
supported_interfaces);
if (self->pv->disco) {
if (self->pv->disco->domain_name)
realm_kerberos_set_domain_name (self, self->pv->disco->domain_name);
if (self->pv->disco->kerberos_realm)
realm_kerberos_set_realm_name (self, self->pv->disco->kerberos_realm);
}
}
static void
realm_kerberos_get_property (GObject *obj,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
RealmKerberos *self = REALM_KERBEROS (obj);
switch (prop_id) {
case PROP_NAME:
g_value_set_string (value, realm_kerberos_get_name (self));
break;
case PROP_DISCO:
g_value_set_boxed (value, realm_kerberos_get_disco (self));
break;
case PROP_MANAGES_SYSTEM:
g_value_set_boolean (value, realm_kerberos_get_manages_system (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
realm_kerberos_set_property (GObject *obj,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
RealmKerberos *self = REALM_KERBEROS (obj);
switch (prop_id) {
case PROP_NAME:
realm_dbus_realm_set_name (self->pv->realm_iface,
g_value_get_string (value));
break;
case PROP_DISCO:
realm_kerberos_set_disco (self, g_value_get_boxed (value));
break;
case PROP_PROVIDER:
/* ignore */
break;
case PROP_MANAGES_SYSTEM:
realm_kerberos_set_manages_system (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
realm_kerberos_finalize (GObject *obj)
{
RealmKerberos *self = REALM_KERBEROS (obj);
g_object_unref (self->pv->realm_iface);
g_object_unref (self->pv->kerberos_iface);
if (self->pv->membership_iface)
g_object_unref (self->pv->membership_iface);
if (self->pv->disco)
realm_disco_unref (self->pv->disco);
G_OBJECT_CLASS (realm_kerberos_parent_class)->finalize (obj);
}
static void
realm_kerberos_class_init (RealmKerberosClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GDBusObjectSkeletonClass *skeleton_class = G_DBUS_OBJECT_SKELETON_CLASS (klass);
object_class->constructed = realm_kerberos_constructed;
object_class->get_property = realm_kerberos_get_property;
object_class->set_property = realm_kerberos_set_property;
object_class->finalize = realm_kerberos_finalize;
skeleton_class->authorize_method = realm_kerberos_authorize_method;
g_type_class_add_private (klass, sizeof (RealmKerberosPrivate));
g_object_class_install_property (object_class, PROP_NAME,
g_param_spec_string ("name", "Name", "Name",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_DISCO,
g_param_spec_boxed ("disco", "Discovery", "Discovery Data",
REALM_TYPE_DISCO, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_PROVIDER,
g_param_spec_object ("provider", "Provider", "Realm Provider",
REALM_TYPE_PROVIDER, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_MANAGES_SYSTEM,
g_param_spec_boolean ("manages-system", "Manages System", "Whether domain configured to manage system",
FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
void
realm_kerberos_set_disco (RealmKerberos *self,
RealmDisco *disco)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
if (disco)
realm_disco_ref (disco);
realm_disco_unref (self->pv->disco);
self->pv->disco = disco;
g_object_notify (G_OBJECT (self), "disco");
}
RealmDisco *
realm_kerberos_get_disco (RealmKerberos *self)
{
RealmKerberosClass *klass;
RealmDisco *disco;
g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL);
if (!self->pv->disco) {
disco = realm_disco_new (NULL);
if (!disco->domain_name)
disco->domain_name = g_strdup (realm_kerberos_get_domain_name (self));
if (!disco->kerberos_realm)
disco->kerberos_realm = g_strdup (realm_kerberos_get_realm_name (self));
klass = REALM_KERBEROS_GET_CLASS (self);
if (klass && klass->discover_myself)
(klass->discover_myself) (self, disco);
self->pv->disco = disco;
}
return self->pv->disco;
}
gchar **
realm_kerberos_parse_logins (RealmKerberos *self,
gboolean lower,
const gchar **logins,
GError **error)
{
const gchar *failed = NULL;
const gchar *const *formats;
gchar **result;
g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL);
formats = realm_dbus_realm_get_login_formats (self->pv->realm_iface);
if (formats == NULL) {
g_set_error (error, REALM_ERROR,
REALM_ERROR_NOT_CONFIGURED,
_("The realm does not allow specifying logins"));
return NULL;
}
result = realm_login_name_parse_all (formats, lower, logins, &failed);
if (result == NULL) {
g_set_error (error, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
_("Invalid login argument%s%s%s does not match the login format."),
failed ? " '" : "", failed, failed ? "'" : "");
}
return result;
}
gchar *
realm_kerberos_format_login (RealmKerberos *self,
const gchar *user)
{
const gchar *const *formats;
g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL);
g_return_val_if_fail (user != NULL, NULL);
formats = realm_dbus_realm_get_login_formats (self->pv->realm_iface);
if (formats == NULL || formats[0] == NULL)
return NULL;
return realm_login_name_format (formats[0], user);
}
static void
set_krb5_error (GError **error,
krb5_error_code code,
krb5_context context,
const gchar *message,
...) G_GNUC_PRINTF (4, 5);
static void
set_krb5_error (GError **error,
krb5_error_code code,
krb5_context context,
const gchar *message,
...)
{
gchar *string;
va_list va;
va_start (va, message);
string = g_strdup_vprintf (message, va);
va_end (va);
g_set_error (error, REALM_KRB5_ERROR, code,
"%s: %s", string, krb5_get_error_message (context, code));
g_free (string);
}
const gchar *
realm_kerberos_get_name (RealmKerberos *self)
{
g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL);
return realm_dbus_realm_get_name (self->pv->realm_iface);
}
const gchar *
realm_kerberos_get_realm_name (RealmKerberos *self)
{
g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL);
return realm_dbus_kerberos_get_realm_name (self->pv->kerberos_iface);
}
void
realm_kerberos_set_realm_name (RealmKerberos *self,
const gchar *value)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
realm_dbus_kerberos_set_realm_name (self->pv->kerberos_iface, value);
}
const gchar *
realm_kerberos_get_domain_name (RealmKerberos *self)
{
g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL);
return realm_dbus_kerberos_get_domain_name (self->pv->kerberos_iface);
}
void
realm_kerberos_set_domain_name (RealmKerberos *self,
const gchar *value)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
realm_dbus_kerberos_set_domain_name (self->pv->kerberos_iface, value);
}
gboolean
realm_kerberos_get_manages_system (RealmKerberos *self)
{
g_return_val_if_fail (REALM_IS_KERBEROS (self), FALSE);
return (self == realm_which_manages_system);
}
void
realm_kerberos_set_manages_system (RealmKerberos *self,
gboolean manages)
{
GObject *obj;
g_return_if_fail (REALM_IS_KERBEROS (self));
if (manages == realm_kerberos_get_manages_system (self))
return;
if (realm_which_manages_system) {
obj = G_OBJECT (realm_which_manages_system);
g_object_remove_weak_pointer (G_OBJECT (obj), (gpointer *)&realm_which_manages_system);
realm_which_manages_system = NULL;
g_object_notify (obj, "manages-system");
}
if (manages) {
obj = G_OBJECT (self);
realm_which_manages_system = self;
g_object_add_weak_pointer (G_OBJECT (obj), (gpointer *)&realm_which_manages_system);
g_object_notify (obj, "manages-system");
}
}
RealmKerberos *
realm_kerberos_which_manages_system (void)
{
return realm_which_manages_system;
}
gboolean
realm_kerberos_matches (RealmKerberos *self,
const gchar *string)
{
const gchar *value;
g_return_val_if_fail (REALM_IS_KERBEROS (self), FALSE);
value = realm_dbus_realm_get_name (self->pv->realm_iface);
if (value != NULL && g_utf8_collate (value, string) == 0)
return TRUE;
value = realm_dbus_kerberos_get_domain_name (self->pv->kerberos_iface);
if (value != NULL && g_utf8_collate (value, string) == 0)
return TRUE;
value = realm_dbus_kerberos_get_realm_name (self->pv->kerberos_iface);
if (value != NULL && g_utf8_collate (value, string) == 0)
return TRUE;
return FALSE;
}
void
realm_kerberos_set_suggested_admin (RealmKerberos *self,
const gchar *value)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
g_return_if_fail (self->pv->membership_iface != NULL);
realm_dbus_kerberos_membership_set_suggested_administrator (self->pv->membership_iface, value);
}
void
realm_kerberos_set_permitted_logins (RealmKerberos *self,
const gchar **value)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
realm_dbus_realm_set_permitted_logins (self->pv->realm_iface, (const gchar * const*)value);
}
void
realm_kerberos_set_permitted_groups (RealmKerberos *self,
const gchar **value)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
realm_dbus_realm_set_permitted_groups (self->pv->realm_iface, (const gchar * const*)value);
}
const gchar *
realm_kerberos_login_policy_to_string (RealmKerberosLoginPolicy value)
{
switch (value) {
case REALM_KERBEROS_ALLOW_ANY_LOGIN:
return REALM_DBUS_LOGIN_POLICY_ANY;
case REALM_KERBEROS_ALLOW_REALM_LOGINS:
return REALM_DBUS_LOGIN_POLICY_REALM;
case REALM_KERBEROS_ALLOW_PERMITTED_LOGINS:
return REALM_DBUS_LOGIN_POLICY_PERMITTED;
case REALM_KERBEROS_DENY_ANY_LOGIN:
return REALM_DBUS_LOGIN_POLICY_DENY;
case REALM_KERBEROS_POLICY_NOT_SET:
return "";
default:
g_return_val_if_reached ("");
}
}
void
realm_kerberos_set_login_policy (RealmKerberos *self,
RealmKerberosLoginPolicy value)
{
realm_dbus_realm_set_login_policy (self->pv->realm_iface,
realm_kerberos_login_policy_to_string (value));
}
void
realm_kerberos_set_login_formats (RealmKerberos *self,
const gchar **value)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
realm_dbus_realm_set_login_formats (self->pv->realm_iface, (const gchar * const*)value);
}
void
realm_kerberos_set_details (RealmKerberos *self,
...)
{
GPtrArray *tuples;
GVariant *tuple;
GVariant *details;
const gchar *name;
const gchar *value;
GVariant *values[2];
va_list va;
g_return_if_fail (REALM_IS_KERBEROS (self));
va_start (va, self);
tuples = g_ptr_array_new ();
for (;;) {
name = va_arg (va, const gchar *);
if (name == NULL)
break;
value = va_arg (va, const gchar *);
if (value == NULL) {
va_end (va);
g_return_if_reached ();
}
values[0] = g_variant_new_string (name);
values[1] = g_variant_new_string (value);
tuple = g_variant_new_tuple (values, 2);
g_ptr_array_add (tuples, tuple);
}
va_end (va);
details = g_variant_new_array (G_VARIANT_TYPE ("(ss)"),
(GVariant * const *)tuples->pdata,
tuples->len);
realm_dbus_realm_set_details (self->pv->realm_iface, details);
g_ptr_array_free (tuples, TRUE);
}
gboolean
realm_kerberos_is_configured (RealmKerberos *self)
{
const gchar *configured;
g_return_val_if_fail (REALM_IS_KERBEROS (self), FALSE);
configured = realm_dbus_realm_get_configured (self->pv->realm_iface);
return configured && !g_str_equal (configured, "");
}
void
realm_kerberos_set_configured (RealmKerberos *self,
gboolean configured)
{
g_return_if_fail (REALM_IS_KERBEROS (self));
realm_dbus_realm_set_configured (self->pv->realm_iface,
configured ? REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE : "");
}
void
realm_kerberos_set_required_package_sets (RealmKerberos *self,
const gchar **package_sets)
{
gchar **packages;
g_return_if_fail (REALM_IS_KERBEROS (self));
packages = realm_packages_expand_sets (package_sets);
realm_dbus_realm_set_required_packages (self->pv->realm_iface, (const gchar **)packages);
g_strfreev (packages);
}
static gboolean
flush_keytab_entries (krb5_context ctx,
krb5_keytab keytab,
krb5_principal realm_princ,
int *remaining,
GError **error)
{
krb5_error_code code;
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
int count = 0;
code = krb5_kt_start_seq_get (ctx, keytab, &cursor);
if (code == KRB5_KT_END || code == ENOENT ) {
*remaining = 0;
return TRUE;
}
while (!krb5_kt_next_entry (ctx, keytab, &entry, &cursor)) {
count++;
if (krb5_realm_compare (ctx, realm_princ, entry.principal)) {
code = krb5_kt_end_seq_get (ctx, keytab, &cursor);
return_val_if_krb5_failed (ctx, code, FALSE);
code = krb5_kt_remove_entry (ctx, keytab, &entry);
return_val_if_krb5_failed (ctx, code, FALSE);
code = krb5_kt_start_seq_get (ctx, keytab, &cursor);
return_val_if_krb5_failed (ctx, code, FALSE);
count = 0;
}
code = krb5_kt_free_entry (ctx, &entry);
return_val_if_krb5_failed (ctx, code, FALSE);
}
code = krb5_kt_end_seq_get (ctx, keytab, &cursor);
return_val_if_krb5_failed (ctx, code, FALSE);
*remaining = count;
return TRUE;
}
gboolean
realm_kerberos_flush_keytab (const gchar *realm_name,
GError **error)
{
char kt_name[MAX_KEYTAB_NAME_LEN];
krb5_error_code code;
krb5_keytab keytab;
krb5_context ctx;
krb5_principal princ;
int remaining;
gchar *name;
gboolean ret;
code = krb5_init_context (&ctx);
if (code != 0) {
set_krb5_error (error, code, NULL, "Couldn't initialize kerberos");
return FALSE;
}
code = krb5_kt_default (ctx, &keytab);
if (code != 0) {
set_krb5_error (error, code, NULL, "Couldn't open default host keytab");
krb5_free_context (ctx);
return FALSE;
}
name = g_strdup_printf ("user@%s", realm_name);
code = krb5_parse_name (ctx, name, &princ);
return_val_if_krb5_failed (ctx, code, FALSE);
g_free (name);
ret = flush_keytab_entries (ctx, keytab, princ, &remaining, error);
krb5_free_principal (ctx, princ);
if (ret && remaining == 0) {
code = krb5_kt_get_name (ctx, keytab, kt_name, sizeof (kt_name));
return_val_if_krb5_failed (ctx, code, FALSE);
}
code = krb5_kt_close (ctx, keytab);
warn_if_krb5_failed (ctx, code);
krb5_free_context (ctx);
if (ret && remaining == 0) {
if (strncmp (kt_name, "FILE:", 5) == 0) {
if (g_unlink (kt_name + 5) < 0 && errno != ENOENT) {
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
"Couldn't remove empty host keytab");
ret = FALSE;
}
}
}
return ret;
}
gchar *
realm_kerberos_get_netbios_name_from_keytab (const gchar *realm_name)
{
krb5_error_code code;
krb5_keytab keytab = NULL;
krb5_context ctx;
krb5_kt_cursor cursor = NULL;
krb5_keytab_entry entry;
krb5_principal realm_princ = NULL;
gchar *princ_name = NULL;
gchar *netbios_name = NULL;
krb5_data *name_data;
code = krb5_init_context (&ctx);
if (code != 0) {
return NULL;
}
princ_name = g_strdup_printf ("user@%s", realm_name);
code = krb5_parse_name (ctx, princ_name, &realm_princ);
g_free (princ_name);
if (code == 0) {
code = krb5_kt_default (ctx, &keytab);
}
if (code == 0) {
code = krb5_kt_start_seq_get (ctx, keytab, &cursor);
}
if (code == 0) {
while (!krb5_kt_next_entry (ctx, keytab, &entry, &cursor) && netbios_name == NULL) {
if (krb5_realm_compare (ctx, realm_princ, entry.principal)) {
name_data = krb5_princ_component (ctx, entry.principal, 0);
if (name_data != NULL
&& name_data->length > 0
&& name_data->data[name_data->length - 1] == '$') {
netbios_name = g_strndup (name_data->data, name_data->length - 1);
if (netbios_name == NULL) {
code = krb5_kt_free_entry (ctx, &entry);
warn_if_krb5_failed (ctx, code);
break;
}
}
}
code = krb5_kt_free_entry (ctx, &entry);
warn_if_krb5_failed (ctx, code);
}
}
code = krb5_kt_end_seq_get (ctx, keytab, &cursor);
warn_if_krb5_failed (ctx, code);
code = krb5_kt_close (ctx, keytab);
warn_if_krb5_failed (ctx, code);
krb5_free_principal (ctx, realm_princ);
krb5_free_context (ctx);
return netbios_name;
}