static errno_t sbus_server_bus_get_connection_unix_process_id(TALLOC_CTX *mem_ctx, struct sbus_request *sbus_req, struct sbus_server *server, const char *name, uint32_t *_pid) { struct sbus_connection *conn; unsigned long pid; dbus_bool_t dbret; if (strcmp(name, DBUS_SERVICE_DBUS) == 0) { *_pid = getpid(); return EOK; } conn = sss_ptr_hash_lookup(server->names, name, struct sbus_connection); if (conn == NULL) { return ERR_SBUS_UNKNOWN_OWNER; } dbret = dbus_connection_get_unix_process_id(conn->connection, &pid); if (!dbret) { return EIO; } *_pid = (uint32_t)pid; return EOK; }
static dbus_bool_t bus_driver_handle_get_connection_unix_process_id (DBusConnection *connection, BusTransaction *transaction, DBusMessage *message, DBusError *error) { const char *service; DBusString str; BusRegistry *registry; BusService *serv; DBusConnection *conn; DBusMessage *reply; unsigned long pid; dbus_uint32_t pid32; _DBUS_ASSERT_ERROR_IS_CLEAR (error); registry = bus_connection_get_registry (connection); service = NULL; reply = NULL; if (! dbus_message_get_args (message, error, DBUS_TYPE_STRING, &service, DBUS_TYPE_INVALID)) goto failed; _dbus_verbose ("asked for PID of connection %s\n", service); _dbus_string_init_const (&str, service); serv = bus_registry_lookup (registry, &str); if (serv == NULL) { dbus_set_error (error, DBUS_ERROR_NAME_HAS_NO_OWNER, "Could not get PID of name '%s': no such name", service); goto failed; } conn = bus_service_get_primary_owners_connection (serv); reply = dbus_message_new_method_return (message); if (reply == NULL) goto oom; if (!dbus_connection_get_unix_process_id (conn, &pid)) { dbus_set_error (error, DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "Could not determine PID for '%s'", service); goto failed; } pid32 = pid; if (! dbus_message_append_args (reply, DBUS_TYPE_UINT32, &pid32, DBUS_TYPE_INVALID)) goto oom; if (! bus_transaction_send_from_driver (transaction, connection, reply)) goto oom; dbus_message_unref (reply); return TRUE; oom: BUS_SET_OOM (error); failed: _DBUS_ASSERT_ERROR_IS_SET (error); if (reply) dbus_message_unref (reply); return FALSE; }
/** * session_from_dbus: * @parent: parent, * @message: D-Bus message. * * Create a new session, based on the specified D-Bus message. * * Return new Session, or NULL on error. **/ Session * session_from_dbus (const void *parent, NihDBusMessage *message) { const char *sender; DBusError dbus_error; unsigned long unix_user; unsigned long unix_process_id; char root[PATH_MAX]; Session *session; struct passwd *pwd; nih_local char *conf_path = NULL; nih_assert (message != NULL); /* Handle explicit command-line request and alternative request * method (primarily for test framework) to disable session support. */ if (disable_sessions || getenv ("UPSTART_NO_SESSIONS")) return NULL; session_init (); /* Ask D-Bus nicely for the origin uid and/or pid of the caller; * sadly we can't ask the bus daemon for the origin pid, so that * one will just have to stay user-session only. */ dbus_error_init (&dbus_error); sender = dbus_message_get_sender (message->message); if (sender) { unix_user = dbus_bus_get_unix_user (message->connection, sender, &dbus_error); if (unix_user == (unsigned long)-1) { dbus_error_free (&dbus_error); unix_user = 0; } unix_process_id = 0; } else { if (! dbus_connection_get_unix_user (message->connection, &unix_user)) unix_process_id = 0; if (! dbus_connection_get_unix_process_id (message->connection, &unix_process_id)) unix_process_id = 0; } /* If we retrieved a process id, look up the root path for it; * if it's just '/' don't worry so much about it. */ if (unix_process_id) { nih_local char *symlink = NULL; ssize_t len; symlink = NIH_MUST (nih_sprintf (NULL, "/proc/%lu/root", unix_process_id)); len = readlink (symlink, root, sizeof root); if (len < 0) return NULL; root[len] = '\0'; if (! strcmp (root, "/")) { unix_process_id = 0; if (! unix_user) return NULL; } } else if (! unix_user) { /* No process id or user id found, return the NULL session */ return NULL; } if (unix_user) { pwd = getpwuid (unix_user); if (! pwd || ! pwd->pw_dir) { nih_error ("%lu: %s: %s", unix_user, _("Unable to lookup home directory"), strerror (errno)); return NULL; } NIH_MUST (nih_strcat_sprintf (&conf_path, NULL, "%s/%s", pwd->pw_dir, USERCONFDIR)); } /* Now find in the existing Sessions list */ NIH_LIST_FOREACH_SAFE (sessions, iter) { Session *session = (Session *)iter; if (unix_process_id) { if (! session->chroot) continue; /* ignore sessions relating to other chroots */ if (strcmp (session->chroot, root)) continue; } /* ignore sessions relating to other users */ if (unix_user != session->user) continue; /* Found a user with the same uid but different * conf_dir to the existing session user. Either the * original user has been deleted and a new user created * with the same uid, or the original users home * directory has changed since they first started * running jobs. Whatever the reason, we (can only) honour * the new value. * * Since multiple users with the same uid are considered * to be "the same user", invalidate the old path, * allowing the correct new path to be set below. * * Note that there may be a possibility of trouble if * the scenario relates to a deleted user and that original * user still has jobs running. However, if that were the * case, those jobs would likely fail anyway since they * would have no working directory due to the original * users home directory being deleted/changed/made inaccessible. */ if (unix_user && conf_path && session->conf_path && strcmp (conf_path, session->conf_path)) { nih_free (session->conf_path); session->conf_path = NULL; } if (! session->conf_path) session_create_conf_source (session); return session; }
/** * Check if Apparmor security controls allow the message to be sent to a * particular connection based on the security context of the sender and * that of the receiver. The destination connection need not be the * addressed recipient, it could be an "eavesdropper" * * @param sender the sender of the message. * @param proposed_recipient the connection the message is to be sent to. * @param requested_reply TRUE if the message is a reply requested by * proposed_recipient * @param bustype name of the bus * @param msgtype message type (DBUS_MESSAGE_TYPE_METHOD_CALL, etc.) * @param path object path the message should be sent to * @param interface the type of the object instance * @param member the member of the object * @param error_name the name of the error if the message type is error * @param destination name that the message should be sent to * @param source name that the message should be sent from * @param error the reason for failure when FALSE is returned * @returns TRUE if the message is permitted */ dbus_bool_t bus_apparmor_allows_send (DBusConnection *sender, DBusConnection *proposed_recipient, dbus_bool_t requested_reply, const char *bustype, int msgtype, const char *path, const char *interface, const char *member, const char *error_name, const char *destination, const char *source, BusActivationEntry *activation_entry, DBusError *error) { #ifdef HAVE_APPARMOR BusAppArmorConfinement *src_con = NULL, *dst_con = NULL; DBusString qstr, auxdata; int src_allow = FALSE, dst_allow = FALSE; int src_audit = TRUE, dst_audit = TRUE; dbus_bool_t free_auxdata = FALSE; unsigned long pid; int len, res, src_errno = 0, dst_errno = 0; uint32_t src_perm = AA_DBUS_SEND, dst_perm = AA_DBUS_RECEIVE; const char *msgtypestr = dbus_message_type_to_string(msgtype); const char *dst_label = NULL; const char *dst_mode = NULL; if (!apparmor_enabled) return TRUE; _dbus_assert (sender != NULL); src_con = bus_connection_dup_apparmor_confinement (sender); if (proposed_recipient) { dst_con = bus_connection_dup_apparmor_confinement (proposed_recipient); } else if (activation_entry != NULL) { dst_label = bus_activation_entry_get_assumed_apparmor_label (activation_entry); } else { dst_con = bus_con; bus_apparmor_confinement_ref (dst_con); } if (dst_con != NULL) { dst_label = dst_con->label; dst_mode = dst_con->mode; } /* map reply messages to initial send and receive permission. That is * permission to receive a message from X grants permission to reply to X. * And permission to send a message to Y grants permission to receive a reply * from Y. Note that this only applies to requested replies. Unrequested * replies still require a policy query. */ if (requested_reply) { /* ignore requested reply messages and let dbus reply mapping handle them * as the send was already allowed */ src_allow = TRUE; dst_allow = TRUE; goto out; } if (is_unconfined (src_con->label, src_con->mode)) { src_allow = TRUE; src_audit = FALSE; } else { if (!_dbus_string_init (&qstr)) goto oom; if (!build_message_query (&qstr, src_con->label, bustype, destination, dst_label, path, interface, member)) { _dbus_string_free (&qstr); goto oom; } res = aa_query_label (src_perm, _dbus_string_get_data (&qstr), _dbus_string_get_length (&qstr), &src_allow, &src_audit); _dbus_string_free (&qstr); if (res == -1) { src_errno = errno; set_error_from_query_errno (error, src_errno); goto audit; } } /* When deciding whether we can activate a service, we only check that * we are allowed to send a message to it, not that it is allowed to * receive that message from us. */ if (activation_entry != NULL || is_unconfined (dst_label, dst_mode)) { dst_allow = TRUE; dst_audit = FALSE; } else { if (!_dbus_string_init (&qstr)) goto oom; if (!build_message_query (&qstr, dst_label, bustype, source, src_con->label, path, interface, member)) { _dbus_string_free (&qstr); goto oom; } res = aa_query_label (dst_perm, _dbus_string_get_data (&qstr), _dbus_string_get_length (&qstr), &dst_allow, &dst_audit); _dbus_string_free (&qstr); if (res == -1) { dst_errno = errno; set_error_from_query_errno (error, dst_errno); goto audit; } } /* Don't fail operations on profiles in complain mode */ if (modestr_is_complain (src_con->mode)) src_allow = TRUE; if (modestr_is_complain (dst_mode)) dst_allow = TRUE; if (!src_allow || !dst_allow) set_error_from_denied_message (error, sender, proposed_recipient, requested_reply, msgtypestr, path, interface, member, error_name, destination); /* Don't audit the message if one of the following conditions is true: * 1) The AppArmor query indicates that auditing should not happen. * 2) The message is a reply type. Reply message are not audited because * the AppArmor policy language does not have the notion of a reply * message. Unrequested replies will be silently discarded if the sender * does not have permission to send to the receiver or if the receiver * does not have permission to receive from the sender. */ if ((!src_audit && !dst_audit) || (msgtype == DBUS_MESSAGE_TYPE_METHOD_RETURN || msgtype == DBUS_MESSAGE_TYPE_ERROR)) goto out; audit: if (!_dbus_string_init (&auxdata)) goto oom; free_auxdata = TRUE; if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) goto oom; if (path && !_dbus_append_pair_str (&auxdata, "path", path)) goto oom; if (interface && !_dbus_append_pair_str (&auxdata, "interface", interface)) goto oom; if (member && !_dbus_append_pair_str (&auxdata, "member", member)) goto oom; if (error_name && !_dbus_append_pair_str (&auxdata, "error_name", error_name)) goto oom; len = _dbus_string_get_length (&auxdata); if (src_audit) { if (!_dbus_append_mask (&auxdata, src_perm)) goto oom; if (destination && !_dbus_append_pair_str (&auxdata, "name", destination)) goto oom; if (sender && dbus_connection_get_unix_process_id (sender, &pid) && !_dbus_append_pair_uint (&auxdata, "pid", pid)) goto oom; if (src_con->label && !_dbus_append_pair_str (&auxdata, "label", src_con->label)) goto oom; if (proposed_recipient && dbus_connection_get_unix_process_id (proposed_recipient, &pid) && !_dbus_append_pair_uint (&auxdata, "peer_pid", pid)) goto oom; if (dst_label && !_dbus_append_pair_str (&auxdata, "peer_label", dst_label)) goto oom; if (src_errno && !_dbus_append_pair_str (&auxdata, "info", strerror (src_errno))) goto oom; if (dst_errno && !_dbus_append_pair_str (&auxdata, "peer_info", strerror (dst_errno))) goto oom; log_message (src_allow, msgtypestr, &auxdata); } if (dst_audit) { _dbus_string_set_length (&auxdata, len); if (source && !_dbus_append_pair_str (&auxdata, "name", source)) goto oom; if (!_dbus_append_mask (&auxdata, dst_perm)) goto oom; if (proposed_recipient && dbus_connection_get_unix_process_id (proposed_recipient, &pid) && !_dbus_append_pair_uint (&auxdata, "pid", pid)) goto oom; if (dst_label && !_dbus_append_pair_str (&auxdata, "label", dst_label)) goto oom; if (sender && dbus_connection_get_unix_process_id (sender, &pid) && !_dbus_append_pair_uint (&auxdata, "peer_pid", pid)) goto oom; if (src_con->label && !_dbus_append_pair_str (&auxdata, "peer_label", src_con->label)) goto oom; if (dst_errno && !_dbus_append_pair_str (&auxdata, "info", strerror (dst_errno))) goto oom; if (src_errno && !_dbus_append_pair_str (&auxdata, "peer_info", strerror (src_errno))) goto oom; log_message (dst_allow, msgtypestr, &auxdata); } out: if (src_con != NULL) bus_apparmor_confinement_unref (src_con); if (dst_con != NULL) bus_apparmor_confinement_unref (dst_con); if (free_auxdata) _dbus_string_free (&auxdata); return src_allow && dst_allow; oom: if (error != NULL && !dbus_error_is_set (error)) BUS_SET_OOM (error); src_allow = FALSE; dst_allow = FALSE; goto out; #else return TRUE; #endif /* HAVE_APPARMOR */ }
/** * Returns true if the given connection can acquire a service, * using the tasks security context * * @param connection connection that wants to own the service * @param bustype name of the bus * @param service_name the name of the service to acquire * @param error the reason for failure when FALSE is returned * @returns TRUE if acquire is permitted */ dbus_bool_t bus_apparmor_allows_acquire_service (DBusConnection *connection, const char *bustype, const char *service_name, DBusError *error) { #ifdef HAVE_APPARMOR BusAppArmorConfinement *con = NULL; DBusString qstr, auxdata; dbus_bool_t free_auxdata = FALSE; /* the AppArmor API uses pointers to int for pointers to boolean, and * int is not strictly guaranteed to be the same as dbus_bool_t */ int allow = FALSE, audit = TRUE; unsigned long pid; int res, serrno = 0; if (!apparmor_enabled) return TRUE; _dbus_assert (connection != NULL); con = bus_connection_dup_apparmor_confinement (connection); if (is_unconfined (con->label, con->mode)) { allow = TRUE; audit = FALSE; goto out; } if (!_dbus_string_init (&qstr)) goto oom; if (!build_service_query (&qstr, con->label, bustype, service_name)) { _dbus_string_free (&qstr); goto oom; } res = aa_query_label (AA_DBUS_BIND, _dbus_string_get_data (&qstr), _dbus_string_get_length (&qstr), &allow, &audit); _dbus_string_free (&qstr); if (res == -1) { serrno = errno; set_error_from_query_errno (error, serrno); goto audit; } /* Don't fail operations on profiles in complain mode */ if (modestr_is_complain (con->mode)) allow = TRUE; if (!allow) dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, "Connection \"%s\" is not allowed to own the service " "\"%s\" due to AppArmor policy", bus_connection_is_active (connection) ? bus_connection_get_name (connection) : "(inactive)", service_name); if (!audit) goto out; audit: if (!_dbus_string_init (&auxdata)) goto oom; free_auxdata = TRUE; if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) goto oom; if (!_dbus_append_pair_str (&auxdata, "name", service_name)) goto oom; if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno))) goto oom; if (!_dbus_append_mask (&auxdata, AA_DBUS_BIND)) goto oom; if (connection && dbus_connection_get_unix_process_id (connection, &pid) && !_dbus_append_pair_uint (&auxdata, "pid", pid)) goto oom; if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label)) goto oom; log_message (allow, "bind", &auxdata); out: if (con != NULL) bus_apparmor_confinement_unref (con); if (free_auxdata) _dbus_string_free (&auxdata); return allow; oom: if (error != NULL && !dbus_error_is_set (error)) BUS_SET_OOM (error); allow = FALSE; goto out; #else return TRUE; #endif /* HAVE_APPARMOR */ }
/** * Check if Apparmor security controls allow the connection to eavesdrop on * other connections. * * @param connection the connection attempting to eavesdrop. * @param bustype name of the bus * @param error the reason for failure when FALSE is returned * @returns TRUE if eavesdropping is permitted */ dbus_bool_t bus_apparmor_allows_eavesdropping (DBusConnection *connection, const char *bustype, DBusError *error) { #ifdef HAVE_APPARMOR BusAppArmorConfinement *con = NULL; DBusString qstr, auxdata; int allow = FALSE, audit = TRUE; dbus_bool_t free_auxdata = FALSE; unsigned long pid; int res, serrno = 0; if (!apparmor_enabled) return TRUE; con = bus_connection_dup_apparmor_confinement (connection); if (is_unconfined (con->label, con->mode)) { allow = TRUE; audit = FALSE; goto out; } if (!_dbus_string_init (&qstr)) goto oom; if (!build_eavesdrop_query (&qstr, con->label, bustype)) { _dbus_string_free (&qstr); goto oom; } res = aa_query_label (AA_DBUS_EAVESDROP, _dbus_string_get_data (&qstr), _dbus_string_get_length (&qstr), &allow, &audit); _dbus_string_free (&qstr); if (res == -1) { serrno = errno; set_error_from_query_errno (error, serrno); goto audit; } /* Don't fail operations on profiles in complain mode */ if (modestr_is_complain (con->mode)) allow = TRUE; if (!allow) dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, "Connection \"%s\" is not allowed to eavesdrop due to " "AppArmor policy", bus_connection_is_active (connection) ? bus_connection_get_name (connection) : "(inactive)"); if (!audit) goto out; audit: if (!_dbus_string_init (&auxdata)) goto oom; free_auxdata = TRUE; if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) goto oom; if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno))) goto oom; if (!_dbus_append_pair_str (&auxdata, "mask", "eavesdrop")) goto oom; if (connection && dbus_connection_get_unix_process_id (connection, &pid) && !_dbus_append_pair_uint (&auxdata, "pid", pid)) goto oom; if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label)) goto oom; log_message (allow, "eavesdrop", &auxdata); out: if (con != NULL) bus_apparmor_confinement_unref (con); if (free_auxdata) _dbus_string_free (&auxdata); return allow; oom: if (error != NULL && !dbus_error_is_set (error)) BUS_SET_OOM (error); allow = FALSE; goto out; #else return TRUE; #endif /* HAVE_APPARMOR */ }