Username/password authentication strategy

Hi Peter,

The only simplification that I had in mind was to disregard the CN entirely
and replace the PIN with a unique auth-token that they'd have to use to add
a new certificate to their profile.

The storage/processing is not much more complex than with
username/passwords - a profile is just a unique auth-token and a list of
hash strings. Realistically, there's no need to delete anything unless you
revoke their account (delete everything) or a certificate has expired
(remove its hash to force a re-auth).

You could have all auth requests go through a single CGI script, which
would allow you to separate authentication from the actual content. For
example, I have a CGI script called 'serverinfo', and when I make a request
to the script with extra path info, the script is invoked and the extra
path is made available to the script. e.g. gemini://
gemini.susa.net/cgi-bin/serverinfo/something/else.gmi yields variables: -

? GEMINI_URL: gemini://gemini.susa.net/cgi-bin/serverinfo/something/else.gmi
? SERVER_NAME: gemini.susa.net
? SERVER_PROTOCOL: GEMINI
? SERVER_SOFTWARE: gemserv
? SCRIPT_NAME: /cgi-bin/serverinfo
? QUERY_STRING:
? PATH_INFO: /something/else.gmi

The PATH_INFO could then be used by subsequent code to 'direct' the CGI
script to generate the correct content, meaning that all your 'handler'
code could assume that validation has passed, a little more like typical
HTTP based code.

Kevin

On Mon, 20 Jul 2020 at 22:27, Peter Vernigorov <pitr.vern at gmail.com> wrote:

> Hey Kevin,
>
> Yes this solution sounds good, pin is passed as part of query string.
> Username is still visible outside tls session of course.
>
> But I think the bigger problem is that now I need to store usernames,
> pins, all of user?s cert fingerprints and their first seen and last seen
> dates, I would need to build an interface to delete old/lost fingerprints,
> etc. Compare that to basic auth in http, which while might not be pretty,
> involves very little code to implement: check username/password hash
> against ones in db on every request (in a before filter if framework
> supports it) and I?m done.
>
> By the way, your solution is very similar to how client ssh usually works,
> one would login using username/password into a new server or from a new
> client, and then add their client certificate to ~/.authorized_keys. So far
> yours is the best solution, but I wonder if there?s a simpler, more elegant
> way.
>
> On Mon, Jul 20, 2020 at 16:50 Kevin Sangeelee <kevin at susa.net> wrote:
>
>> I see this as the same issue that exists for clients - without a trusted
>> CA, there's nothing to stop me masquerading as anyone else, other than the
>> certificate (e.g. fingerprint) pinning, so I believe the process absolutely
>> needs a (one-time) login prompt to pin the certificate - I can't see a way
>> to avoid it.
>>
>> Your question made me think about what would be the minimum steps
>> required to implement client TOFU with PIN or password verification, and
>> the code below is my take on what I think has to happen. Note the code is
>> only meant as a concise way to discuss the process.
>>
>> So, for example, a request to a private resource, such as: -
>>
>> gemini://susa.net/cgi-bin/private.sh?articleId=1234
>>
>> Might be handled something like this: -
>>
>> $ cat private.sh
>> #!/bin/bash
>>
>> # If we have no client certificate, request one.
>> if [[ "${TLS_CLIENT_HASH}" == "" ]]; then
>>
>>     echo -ne "60 CLIENT CERTIFICATE REQUIRED\r\n"
>>
>> elif [[ $(grep "${TLS_CLIENT_HASH}" /tmp/${REMOTE_USER}_sigs) == "" ]];
>> then
>>
>>     # We've never seen this user's client hash, so challenge for a PIN
>>     # (we could store this current URL for onward forwarding)
>>     echo -ne "30 /cgi-bin/login.sh\r\n"
>>
>> else
>>
>>     echo -ne "20 text/gemini\r\n"
>>     echo "This is the prize inside the protected resource!"
>> fi
>>
>> I think a redirect to a login process is unavoidable, because any
>> originating request might have its own query_string, yet we're going to
>> need one for the password prompt response. So the login.sh would be our
>> one-time verification to pin the certificate to a CN based user-id.
>>
>> $ cat login
>> #!/bin/bash
>>
>> # If we have no client certificate, request one.
>> if [[ "${TLS_CLIENT_HASH}" == "" ]]; then
>>     echo -ne "60 CLIENT CERTIFICATE REQUIRED\r\n"
>>     exit
>> fi
>>
>> # If the certificate has no CN, then reject it
>> if [[ "${REMOTE_USER}" == "" ]]; then
>>     echo -ne "20 text/gemini\r\n"
>>     echo "Your CN can't be used as a user-id!"
>>     exit
>> fi
>>
>> # We have a client certificate, is it already authorised?
>> if [[ $(grep "${TLS_CLIENT_HASH}" /tmp/${REMOTE_USER}_sigs) != "" ]]; then
>>     echo -ne "20 text/gemini\r\n"
>>     echo "You are already authorised!"
>>     exit
>> fi
>>
>> # We have a certificate and a valid user-id (CN), so either prompt for
>> # a PIN, or verify PIN if we've been given a QUERY_STRING.
>>
>> # Grep the remote user's line from the passwd file (format CN:PIN)
>> PWD_ENTRY=$(grep "^${REMOTE_USER}:" login_passwd)
>>
>> # Strip the CN value and separator, leaving the PIN
>> MY_PIN="${PWD_ENTRY#${REMOTE_USER}:}"
>>
>> # Check for our PIN in the query string (which may be empty)
>> if [[ "${QUERY_STRING}" == "${MY_PIN}" ]]; then
>>     echo -ne "20 text/gemini\r\n"
>>     echo "Your PIN checks out, ${REMOTE_USER}!"
>>     echo "${TLS_CLIENT_HASH}" >>/tmp/${REMOTE_USER}_sigs
>>     echo "Your hash has been stored in '/tmp/${REMOTE_USER}_sigs'"
>>     exit
>> fi
>>
>> echo -ne "11 Please enter your PIN, ${REMOTE_USER}\r\n"
>>
>>
>> On Sun, 19 Jul 2020 at 20:26, Peter Vernigorov <pitr.vern at gmail.com>
>> wrote:
>>
>>> I am trying to implement a simple authentication for my Gemini site,
>>> and was planning to use a client certificate CN field to pass
>>> username:password pair to server. However, upon reading closely about
>>> the TLS handshake -
>>> https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_handshake -
>>> it seems that the client (just like the server) certificate is sent
>>> before the ChangeCipherSpec record, i.e. insecure. That means to me
>>> that the CN field would be passed before the TLS session is started
>>> and therefore not suitable as an authentication medium. Is that
>>> correct?
>>>
>>> Another alternative to implement username/password type authentication
>>> in Gemini would be the sensitive input status code, but then I would
>>> have to store a list of certificate fingerprint and username pairs,
>>> greatly complicating the system.
>>>
>>> Given that Gemini protocol strives to be minimal/low-cost for both
>>> users and client/server devs, has anyone found a simple way to
>>> implement username/password type authentication systems? To be fair, I
>>> have attempted to use client short-/long-lived client certificates as
>>> per recommendation in Gemini protocol specification; however, if
>>> access to the same "account" from multiple devices and being able to
>>> survive certificate loss without permanently losing access is my
>>> account are requirements, this authentication method quickly becomes a
>>> mess. For example, think about how one would go about getting access
>>> to their astrobotany plant on a new device. This is why I ended up
>>> going back to username/password authentication, but having
>>> difficulties making sure that everything is secure. In need of
>>> help/suggestions/ideas, thanks.
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.orbitalfox.eu/archives/gemini/attachments/20200720/c699
483b/attachment.htm>

---

Previous in thread (5 of 12): 🗣️ Peter Vernigorov (pitr.vern (a) gmail.com)

Next in thread (7 of 12): 🗣️ Solderpunk (solderpunk (a) posteo.net)

View entire thread.