From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-3.2 required=3.0 tests=AWL,BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_BL_SPAMCOP_NET,RCVD_IN_DNSWL_HI, SPF_HELO_PASS,SPF_PASS shortcircuit=no autolearn=no autolearn_force=no version=3.4.6 Received: from nue.mailmanlists.eu (nue.mailmanlists.eu [IPv6:2a01:4f8:1c0c:6b10::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id 740E21F5A0 for ; Mon, 6 Feb 2023 08:10:31 +0000 (UTC) Authentication-Results: dcvr.yhbt.net; dkim=fail reason="signature verification failed" (1024-bit key; secure) header.d=ml.ruby-lang.org header.i=@ml.ruby-lang.org header.a=rsa-sha256 header.s=mail header.b=ucmtacGI; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ruby-lang.org header.i=@ruby-lang.org header.a=rsa-sha256 header.s=s1 header.b=s4Dvj7Wm; dkim-atps=neutral Received: from nue.mailmanlists.eu (localhost [127.0.0.1]) by nue.mailmanlists.eu (Postfix) with ESMTP id 0402C7D664; Mon, 6 Feb 2023 08:10:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ml.ruby-lang.org; s=mail; t=1675671021; bh=okmAYVp67dkUQI1U3qFiQxj8z8TLlOazw7dgjgL0pJU=; h=Date:References:To:Reply-To:Subject:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Cc:From; b=ucmtacGIveG5CKfZB4+gQeTZzIvCyhaEyJOs9wfqxE8LBibUNB+w2nNdF9m1aYIdf Xi0jGhBeOkV/emaKNuDrKiavAIS2UnrBT36CHFzKI7Tx+0IOmyfywYia9lRNKhFe2R 8fysoqcdkm+SDSNLaTNx5gmnn8r2pttqW3NX0/1w= Received: from xtrwkhkc.outbound-mail.sendgrid.net (xtrwkhkc.outbound-mail.sendgrid.net [167.89.16.28]) by nue.mailmanlists.eu (Postfix) with ESMTPS id BB2987D626 for ; Mon, 6 Feb 2023 08:10:17 +0000 (UTC) Authentication-Results: nue.mailmanlists.eu; dkim=pass (2048-bit key; unprotected) header.d=ruby-lang.org header.i=@ruby-lang.org header.a=rsa-sha256 header.s=s1 header.b=s4Dvj7Wm; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ruby-lang.org; h=from:references:subject:mime-version:content-type: content-transfer-encoding:list-id:to:cc:content-type:from:subject:to; s=s1; bh=uD1AZS4FbOyeCz6/tCJJhREBYRrcIV5vEMw2l4LybIc=; b=s4Dvj7WmRWVe3hO4Kyvw4B1sw6as6bEU8IQYvtc7baKARU9HWAYe8XEk3QXRJxfL8gAb 2lrXXgPpduWQAl8/X+CX3N7BkDQlU9XnjqAjUuBIUtmiPV+qa5ttXYl+kiGshy6MtgXIwI FbFlkhZYPUM38wdK+2Zjb5S3ijZS+b41nU5oKCpe7AloleBRr8Jg76OrkdXgIanFAGCqeJ JpbNws7TSFgshdv8G1VtN0JnAdIBke/XMugfuTh/gHL3/UFPtTQnZaY1BDy7mpOYpscTUa XrhBd1g2UdXNC6LVO1jc/YrJmgyxdSLsQyrTAOSN0At2kHY6DDM5+/Jz0uNwp/qQ== Received: by filterdrecv-6c4ccfbdd8-pvv59 with SMTP id filterdrecv-6c4ccfbdd8-pvv59-1-63E0B5E7-38 2023-02-06 08:10:15.963638865 +0000 UTC m=+6944682.222719775 Received: from herokuapp.com (unknown) by geopod-ismtpd-2-4 (SG) with ESMTP id YRvHAqdVS_q5Q5ZhfEdEww for ; Mon, 06 Feb 2023 08:10:15.848 +0000 (UTC) Date: Mon, 06 Feb 2023 08:10:16 +0000 (UTC) Message-ID: References: Mime-Version: 1.0 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Feature X-Redmine-Issue-Id: 19179 X-Redmine-Issue-Author: kjtsanaktsidis X-Redmine-Sender: akr X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-Redmine-MailingListIntegration-Message-Ids: 88619 X-SG-EID: =?us-ascii?Q?TSUNhpCmVZRsQSpdR1gyEfTyc7krzYeb42OTzCPqqlCYAK8ou4fZ79RnN7jrwO?= =?us-ascii?Q?Lt=2FrDjW1eBDWJA1P21Z8ehYrf4I2GMm2mvLuKQ=2F?= =?us-ascii?Q?A9UxHcuofzB59pMxc8pbyTuAlf+4GtYOOCK9chT?= =?us-ascii?Q?kUbn7dJvDuzG7es8++awZN60sC0iP14NTR9aUai?= =?us-ascii?Q?WYlHIexdqP5HZOoa9+7NuwUjwRLa3edLGMbwcmh?= =?us-ascii?Q?6Mmi1buC0NtEqLN0HKc3zzXjxkDs4dNf4vCPPiK?= =?us-ascii?Q?InfW8sAGiwyLMFfER2ZKw=3D=3D?= To: ruby-core@ml.ruby-lang.org X-Entity-ID: b/2+PoftWZ6GuOu3b0IycA== Message-ID-Hash: FSVKCA4WNVYXJE6MO5KPDMVP67SAODP7 X-Message-ID-Hash: FSVKCA4WNVYXJE6MO5KPDMVP67SAODP7 X-MailFrom: bounces+313651-b711-ruby-core=ml.ruby-lang.org@em5188.ruby-lang.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.3 Precedence: list Reply-To: Ruby developers Subject: [ruby-core:112230] [Ruby master Feature#19179] Support parsing SCM_CRED(ENTIALS) messages from ancillary messages List-Id: Ruby developers Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: "akr (Akira Tanaka) via ruby-core" Cc: "akr (Akira Tanaka)" Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Issue #19179 has been updated by akr (Akira Tanaka). I think this is a good direction. However, class and method names should be reviewed by matz. I doubt that the current proposal has names good enough. For example, "as_ancillary_data" is not in the convention of Ruby. Ruby uses method names "to_*" for conversion methods. ---------------------------------------- Feature #19179: Support parsing SCM_CRED(ENTIALS) messages from ancillary messages https://bugs.ruby-lang.org/issues/19179#change-101653 * Author: kjtsanaktsidis (KJ Tsanaktsidis) * Status: Open * Priority: Normal ---------------------------------------- ## Background Linux and FreeBSD support processes at either end of a unix socket identifying themselves to the other party by passing an ancillary message of type `SCM_CREDENTIALS` (Linux) or `SCM_CREDS` (FreeBSD). The socket library contains code to parse these ancillary messages, but the only way this is exposed into Ruby code is by the `Socket::AncillaryData#inspect` method - e.g. ``` # On Linux irb(main):002:0> s1, s2 = UNIXSocket.pair => [#, #] irb(main):004:0> s2.setsockopt Socket::SOL_SOCKET, Socket::SO_PASSCRED, 1 => 0 # struct ucred on Linux is (32-bit signed) pid_t, followed by (32-bit unsigned) uid_t, followed by # (32-bit unsigned) gid_t irb(main):008:0> ancdata = [Process.pid, Process.uid, Process.gid].pack("lLL") => "\x1ET\x05\x00\xE8\x03\x00\x00\xE8\x03\x00\x00" # Socket::AncillaryData knows how to unmarshal the data into struct ucred irb(main):010:0> ancmsg = Socket::AncillaryData.new(Socket::AF_UNIX, Socket::SOL_SOCKET, Socket::SCM_CRE DENTIALS, ancdata) => # irb(main):011:0> s1.sendmsg "hi", 0, nil, ancmsg => 2 # ancillary message can be passed through irb(main):012:0> _, _, _, recvanc = s2.recvmsg; recvanc => # ``` On Linux, at least, a suitably privileged process can send any value through for the pid, uid, or gid, but the kernel will reject attempts by unprivileged processes to forge credentials in this way. So SCM_CREDENTIALS messages can be useful for certain systems programming tasks. A somewhat wider array of operating systems support querying the identity of the other side of a socket using a socket option, variously `SO_PEERCRED` (Linux, OpenBSD) or `LOCAL_PEERCRED` (FreeBSD, MacOS). Again, the socket library is able to unmarshal the socket data into the correct structure on these various systems, but it's only exposed to Ruby code via `#inspect` - e.g. ``` irb(main):002:0> s1, s2 = UNIXSocket.pair => [#, #] irb(main):014:0> s1.getsockopt Socket::SOL_SOCKET, Socket::SO_PEERCRED => # ``` Ruby _does_ however support e.g. `BasicSocket#getpeereid`, which could use `SO_PEERCRED` etc under the hood - so getting the uid/gid data is not totally impossible. I believe getting the pid is though. ``` irb(main):016:0> s1.getpeereid => [1000, 1000] ``` ## My proposal I believe we should implement the following: * `Socket::Credentials` - this would be a struct which can contain all the various platform-specific pieces of credential info that can be transferred over a socket, such as uid, gid, pid, euid, egid, and group list. * `Socket::AncillaryData#credentials` - this would parse an `SCM_CREDS` or `SCM_CREDENTIALS` ancillary data message into the appropriate platform-specific struct, and return a `Socket::Credentials` instance containing that data. This would be analogous to `Socket::AncillaryData#int`; a method for interpreting the ancillary data in a certain form. * `Socket::Option#credentials` - This would parse a `SO_PEERCRED` or `LOCAL_PEERCRED` socket option response into the appropriate platform-specific struct, and return a `Socket::Credentials` instance containing that data. Again, this would be analogous to `Socket::Option#int`. The existing `struct ucred`/`struct xucred`/`struct sockpeercred`/`struct cmsgcred` parsing code (used only for `#inspect` output) would be moved into `Socket::Credentials`, and `Socket::AncillaryData#inspect`/`Socket::Option#inspect` would be implemented in terms of `Socket::Credentials`. This would nicely wrap a lot of parsing work that Ruby is already doing, into an API which allows Ruby code to take advantage of it. ## Use-cases My motivation for designing this feature came about whilst I was experimenting with some ideas for Ruby profilers. I wanted to allow a CLI tool to ask a Ruby process to start profiling itself by sending a message on a unix socket. Alongside the message, it would send a file descriptor which was the result of calling `perf_event_open(2)` in the CLI tool. In order to call `perf_event_open(2)`, the CLI tool would need to be privileged. I also wanted the Ruby process to authenticate the request and make sure it came from the same UID that it was running as. Calling `BasicSocket#getpeereuid` would reveal the remote process to be running as UID 0, (or perhaps even some other UID, with sufficient ambient capabilities to call `perf_event_open`). Instead, I decided to make the CLI tool send a `SCM_CREDENTIALS` message containing the uid of the process to be profiled; that way, the kernel does all the policy checking on whether or not this is actually allowed, and the Ruby process receiving th e message just needs to check if `uid == Process.getuid`. I think, on Linux at least, that this feature will be useful for any kind of communication/authentication scheme between privileged & unprivileged processes over unix sockets. ## My implementation I have an implementation of roughly this in this pull request: https://github.com/ruby/ruby/pull/6822 Thanks! -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/