Anyone interested in working on adding GSS-API authentication a'la http://www.ietf.org/internet-drafts/draft-ietf-secsh-gsskeyex-06.txt to lsh? I'm currently working on my own GSS library (see http://josefsson.org/gss/) and while far from finished, it can now talk to other clients/servers via GSASL or Mailutils. I'd imagine adding the GSS support to lsh would stress test more stuff in the GSS library, so it looks like a good next step for me. However, as I'm not familiar with lsh (the gc stuff make it look a bit complex) I would appreciate if someone else did the lsh part of the work :-) or at least had an opinion on if doing this is feasible without becoming a lsh guru. Is there some major component missing from lsh that would be needed before the GSS auth/kex idea can be implemented?
(Of course, once written, it will work with other GSS implementations too, like MIT Kerberos 5, Heimdal, Sun, Globus etc.)
Thanks, Simon
Simon Josefsson jas@extundo.com writes:
Anyone interested in working on adding GSS-API authentication a'la http://www.ietf.org/internet-drafts/draft-ietf-secsh-gsskeyex-06.txt to lsh? I'm currently working on my own GSS library (see http://josefsson.org/gss/) and while far from finished, it can now talk to other clients/servers via GSASL or Mailutils.
I'm afraid I haven't kept up with the GSS-API discussions (and I'm generally a little sceptical about it, but I think we've discussed that earlier).
I'd imagine adding the GSS support to lsh would stress test more stuff in the GSS library, so it looks like a good next step for me.
Sounds interesting.
Would you start with the server or the client side? On the server, you need to inherit the userauth class in server_userauth.h, with some guidance from the class userauth_publickey in server_publickey.c. I think that should be reasonably straight forward.
You also have the option of designing a way to put the gssapi processing in a separate program (like the kerberos and pam helper programs), but you probably need a more sophisticated protocol than those. I think that's the way to go for keyboard-interactive (http://bugzilla.lysator.liu.se/show_bug.cgi?id=1028), but it may not be for GSS-API.
In the client, the code is a little hairier (due to the way userauth failures are handled), and it's all in client_userauth.c.
However, as I'm not familiar with lsh (the gc stuff make it look a bit complex)
The point of the stuff is to make things less complex, as you don't need any code for deciding when an object can be deallocated... As for the LSH style of object oriented C, I admit it can take a little time to get used to ;-), but it's fairly simple. The best starting point I can give is the file doc/HACKING in the LSH distribution.
Is there some major component missing from lsh that would be needed before the GSS auth/kex idea can be implemented?
I don't think there's anything missing in the general framework that you'd need for GSS support, but of course it's hard to say for sure before one starts to look into GSS-API in detail.
I personally don't plan to add GSS-API support soon, but if anybody else likes to work on it, I'd be happy to guide you around.
Best regards, /Niels
nisse@lysator.liu.se (Niels Möller) writes:
Simon Josefsson jas@extundo.com writes:
Anyone interested in working on adding GSS-API authentication a'la http://www.ietf.org/internet-drafts/draft-ietf-secsh-gsskeyex-06.txt to lsh? I'm currently working on my own GSS library (see http://josefsson.org/gss/) and while far from finished, it can now talk to other clients/servers via GSASL or Mailutils.
I'm afraid I haven't kept up with the GSS-API discussions (and I'm generally a little sceptical about it, but I think we've discussed that earlier).
I'm also skeptic (I have a "Criticism of GSS" in the manual that discusses some problems), but I haven't seen any standardized approach to support Kerberos 5 in SSH except GSS, so...
Personally I would have used SASL. Fortunately, there is no conflict, and we could add SASL support too.
I'd imagine adding the GSS support to lsh would stress test more stuff in the GSS library, so it looks like a good next step for me.
Sounds interesting.
Would you start with the server or the client side? On the server, you need to inherit the userauth class in server_userauth.h, with some guidance from the class userauth_publickey in server_publickey.c. I think that should be reasonably straight forward.
Good. Clients are often easier to debug though, but I'll follow this pointer first.
You also have the option of designing a way to put the gssapi processing in a separate program (like the kerberos and pam helper programs), but you probably need a more sophisticated protocol than those. I think that's the way to go for keyboard-interactive (http://bugzilla.lysator.liu.se/show_bug.cgi?id=1028), but it may not be for GSS-API.
If/when there is a framework for that, and the protocol is flexible enough to support GSS, I guess it sounds better. I won't look into this now though, since my focus is mostly on the GSS side.
I personally don't plan to add GSS-API support soon, but if anybody else likes to work on it, I'd be happy to guide you around.
Thanks, Simon
I'm sorry for the length of this, if we should keep this off the list, please respond in private.
Simon Josefsson jas@extundo.com writes:
Would you start with the server or the client side? On the server, you need to inherit the userauth class in server_userauth.h, with some guidance from the class userauth_publickey in server_publickey.c. I think that should be reasonably straight forward.
Good. Clients are often easier to debug though, but I'll follow this pointer first.
It was rather straightforward, and I now have interop between lsh in server mode against OpenSSH (with the GSSAPI patches) in client mode. But. Once the authentication finished, lsh more or less crashes. I'm fairly certain this is because I don't understand the OOP stuff, and only have a few hours of experience with lsh in general. Here is output from lshd --debug ran under gdb, and the source code for the new server_gssapi.c. Any ideas? I suspect the error is in how do_handle_gssapi_finish() call COMMAND_RETURN(), or possibly how the gssapi_finish_handler class is defined OOP-wise.
Thanks, Simon
... lshd: write_buffer: do_write length = 48 lshd: write_buffer: do_write closure->length = 48 lshd: DEBUG: Received USERAUTH_REQUEST ***** lshd: handle_connection: Received packet of type 50 (USERAUTH_REQUEST) lshd: DEBUG: Sent USERAUTH_FAILURE lshd: (size 31 = 0x1f) 00000000: 330000001970617373776f72642c7075 3....password,pu 00000010: 626c69636b65792c67737361706900 blickey,gssapi.
lshd: write_buffer: do_write length = 56 lshd: write_buffer: do_write closure->length = 56 lshd: DEBUG: Received USERAUTH_REQUEST ***** lshd: handle_connection: Received packet of type 50 (USERAUTH_REQUEST) hepp 1 lshd: DEBUG: Sent USERAUTH_PK_OK|USERAUTH_PASSWD_CHANGEREQ|USERAUTH_GSSAPI_RESPONSE lshd: (size 16 = 0x10) 00000000: 3c0000000b06092a864886f712010202 <......*.H......
lshd: write_buffer: do_write length = 48 lshd: write_buffer: do_write closure->length = 48 lshd: DEBUG: Received USERAUTH_GSSAPI_TOKEN lshd: (size 1158 = 0x486) 00000000: 3d000004816082047d06092a864886f7 =....`..}..*.H.. 00000010: 1201020201006e82046c30820468a003 ......n..l0..h.. 00000020: 020105a10302010ea207030500200000 ............. .. 00000030: 00a38201286182012430820120a00302 ....(a..$0.. ... 00000040: 0105a10f1b0d4a4f53454653534f4e2e ......JOSEFSSON. 00000050: 4f5247a2263024a003020101a11d301b ORG.&0$.......0. 00000060: 1b04686f73741b136c617474652e6a6f ..host..latte.jo 00000070: 73656673736f6e2e6f7267a381df3081 sefsson.org...0. 00000080: dca003020110a103020103a281cf0481 ................ 00000090: ccf1fed3d9d3aa79c54b7396eaca1259 .......y.Ks....Y 000000a0: 9a58eec0e7682e2a342abdb41eb9486b .X...h.*4*....Hk 000000b0: 0f68fd3489bef45208ef1d140d344ca8 .h.4...R.....4L. 000000c0: f79b388b14e2fe4df259af16b1488921 ..8....M.Y...H.! 000000d0: c344920f1801d09f05782e275acb80a6 .D.......x.'Z... 000000e0: 70914a5bab2892c3b3fc6e8173114d31 p.J[.(....n.s.M1 000000f0: ae944e5e4ec861607152861794540aa5 ..N^N.a`qR...T.. 00000100: f5e092a1d08c28f15fba3ca8e35f18ec ......(._.<.._.. 00000110: d8bdaed7e53fb87a622b3ad8be1a6eff .....?.zb+:...n. 00000120: 41b86c0902d16cd47b130e7d1654e528 A.l...l.{..}.T.( 00000130: 4a43f9bc13849ba5234b457993e493ef JC......#KEy.... 00000140: a53d01fb936e1c373dfeb36b665fa00a .=...n.7=..kf_.. 00000150: d321688ee6bf0b89549f7d8cc3a48203 .!h.....T.}..... 00000160: 2530820321a003020110a28203180482 %0..!........... 00000170: 03142495fcd56ee48a8d3fecace801e8 ..$...n...?..... 00000180: a32c9f8ad73946ae6417e71aa8b801d0 .,...9F.d....... 00000190: 60e5a14e04c9ab9d49c4f368e65e303e `..N....I..h.^0> 000001a0: 23373693a708517c49ad393e9fb60549 #76...Q|I.9>...I 000001b0: 95a95dd066462cb6a033eb0ac70b136c ..].fF,..3.....l 000001c0: c20a3f7289907840f73d89c573f034ef ..?r..x@.=..s.4. 000001d0: 94e15559888626e6edc80bf1e04d8c58 ..UY..&......M.X 000001e0: ae101686e021e4341f9b857219450fc0 .....!.4...r.E.. 000001f0: f0f047b8bc457ba0d74a9004bac72da4 ..G..E{..J....-. 00000200: e45c81b479c42930f91ffa971975fd14 ...y.)0.....u.. 00000210: 6fb621de5fd369931a54cd1ff51b49c4 o.!._.i..T....I. 00000220: f37f190ce6afdc1f5f60ba227c40e674 ........_`."|@.t 00000230: db918f64bce55752a015a2eafed5b6a9 ...d..WR........ 00000240: f4124f68f31e0d46b04c9a435f118d3c ..Oh...F.L.C_..< 00000250: 2478e8e0618696e77529ddb7d340fd97 $x..a...u)...@.. 00000260: 5c84ba4860cfeebfafd12d4d83eb6c03 ..H`.....-M..l. 00000270: 68d66cec779a9771c69b7ee153929dfb h.l.w..q..~.S... 00000280: a80237d06ebba5bded29e07c89439616 ..7.n....).|.C.. 00000290: 2f575b8954eddff5720dff1ea1f8622d /W[.T...r.....b- 000002a0: d8794f1b8a91a8a265a7efc6df990469 .yO.....e......i 000002b0: 02df1e520e834352f0a157a7d0e60854 ...R..CR..W....T 000002c0: aa2cc821540188dddc347c1e199513c8 .,.!T....4|..... 000002d0: 0cd89477f4194b2a45467d9a07070e9a ...w..K*EF}..... 000002e0: 681dd699202f6fde0fba4417dc0e759e h... /o...D...u. 000002f0: 18e2ba96bc7541ea427ea37c96b3fb93 .....uA.B~.|.... 00000300: d95bb79d8655da32a489de26d14fb0c6 .[...U.2...&.O.. 00000310: 10dbf5cd9085e0151051aec23dfbf39b .........Q..=... 00000320: 168b2836fca4c2c7d22a48d6276087b8 ..(6.....*H.'`.. 00000330: 89b9a192b93b381b2cf415cd117a1fa1 .....;8.,....z.. 00000340: f8f5f288289bd8a8718b4c05e7092d26 ....(...q.L...-& 00000350: 2f8613a9763464b9ed8264c3bfcd2ca9 /...v4d...d...,. 00000360: 0926ea97c7289693fce7543eb716ec72 .&...(....T>...r 00000370: 3d25aaed8b5fea53856cfe31e18746a7 =%..._.S.l.1..F. 00000380: f343dea2a6b9ae5a1f21701b51b6decc .C.....Z.!p.Q... 00000390: 06641b1cb1b489572896226a620a9b5b .d.....W(."jb..[ 000003a0: e73272e4a302b693b3cbb37578d08bb9 .2r........ux... 000003b0: ec112aff82384f28966334d3ed6c1534 ..*..8O(.c4..l.4 000003c0: cdb85f01246d55178b54ccb11c43db06 .._.$mU..T...C.. 000003d0: 431fb3aa71da6726daf9d52162c4ceba C...q.g&...!b... 000003e0: 4366685ae0d625453f6bfb39abdb511d CfhZ..%E?k.9..Q. 000003f0: 2e23a6cee059cbcd7053dc33265c3f67 .#...Y..pS.3&?g 00000400: f20fe142156dc48945796c4bf1984be7 ...B.m..EylK..K. 00000410: 349ec5f7ecefb0adfce2f5cb748e7389 4...........t.s. 00000420: 8decad916af1f0249a5b030f3f783753 ....j..$.[..?x7S 00000430: 1a9328b317d5efbadd55ac492e863671 ..(......U.I..6q 00000440: e20b4a9f27e3ab793d86a1640a7049c8 ..J.'..y=..d.pI. 00000450: 435e92958e37ba301893e90072f9427b C^...7.0....r.B{ 00000460: 7ebf24bffc43217014fc840613a29e26 ~.$..C!p.......& 00000470: 1861bf5b06e1cb1e4107a479e84ca871 .a.[....A..y.L.q 00000480: f29fd62a5223 ...*R#
lshd: handle_connection: Received packet of type 61 (USERAUTH_GSSAPI_TOKEN) Error in re-setting breakpoint 1: Function "do_gssapi_done" not defined. Error in re-setting breakpoint 1: Function "do_gssapi_done" not defined. Error in re-setting breakpoint 1: Function "do_gssapi_done" not defined. lshd: DEBUG: Sent USERAUTH_GSSAPI_TOKEN lshd: (size 112 = 0x70) 00000000: 3d0000006b606906092a864886f71201 =...k`i..*.H.... 00000010: 020202006f5a3058a003020105a10302 ....oZ0X........ 00000020: 010fa24c304aa003020110a103020100 ...L0J.......... 00000030: a23e043c83cd60c58db638304549d2e6 .>.<..`...80EI.. 00000040: f3d20db3274657101987f9e8197dfeb8 ....'FW......}.. 00000050: 55987868d3695e737a1e77d2f05360e2 U.xh.i^sz.w..S`. 00000060: 71fea05d534500346b3f76383b2fc2a4 q..]SE.4k?v8;/..
lshd: write_buffer: do_write length = 144 lshd: write_buffer: do_write closure->length = 144 lshd: DEBUG: Received USERAUTH_GSSAPI_EXCHANGE_COMPLETE lshd: (size 1 = 0x1) 00000000: 3f ?
lshd: handle_connection: Received packet of type 63 (USERAUTH_GSSAPI_EXCHANGE_COMPLETE) ok lshd: connection.c:306: do_exc_connection_handler: Assertion `self->connection->paused' failed.
Program received signal SIGABRT, Aborted. 0x400cba41 in kill () from /lib/libc.so.6 (gdb) bt full #0 0x400cba41 in kill () from /lib/libc.so.6 No symbol table info available. #1 0x400cb862 in raise () from /lib/libc.so.6 No symbol table info available. #2 0x400cc976 in abort () from /lib/libc.so.6 No symbol table info available. #3 0x400c5ae9 in __assert_fail () from /lib/libc.so.6 No symbol table info available. #4 0x0805112e in do_exc_connection_handler (s=0x809d898, e=0xbfffad80) at connection.c:312 No locals. #5 0x080516be in connection_unlock (self=0x0) at connection.c:562 unpause = {super = {next = 0x0, isa = 0x0, alloc_method = 1 '\001', marked = 0 '\0', dead = 0 '\0'}, type = 1048580, msg = 0x807d8bd "unlocking connection."} #6 0x0805e082 in do_userauth_continuation (s=0x80b03e0, a=0x401b1f60) at server_userauth.c:145 i = 0 #7 0x0805c642 in do_handle_gssapi_finish (s=0x80bc288, connection=0x809ec68, packet=0x80bc508) at server_gssapi.c:97 self = (struct gssapi_finish_handler *) 0x80bc288 #8 0x08050cec in connection_handle_packet (closure=0x809ec68, packet=0x80bc508) at connection.c:155 msg = 63 '?' #9 0x080522fb in do_debug (w=0x809cfb0, packet=0x80bc508) at debug.c:66 No locals. #10 0x08065ee3 in do_packet_inflate (closure=0x809d008, packet=0x401b1f60) at compress.c:84 No locals. #11 0x08064368 in do_unpad (w=0x809d710, packet=0x80bd080) at unpad.c:97 padding_length = 96 '`' payload_length = 1075519328 new = (struct lsh_string *) 0x80bc508 #12 0x0805a674 in do_read_packet (h=0x809f29c, available=3221204656, data=0xbfffaf00 "�\020\204�\217_!\210�\003��\004�\210\213\005��\213\003'K�|�qOQ6�6.U\217K�\232\f�\v\+s<���:U3\001g\a�1z¨J\030֬%�\020%R\202�\006�\221\022dݡ9�F�\030-R\0236�\027�I����lK~vYh\200\005i\221#KXk�!hw�\225vB\177\203=/~�\036:\236w�\005�\035|0\215u.7�\223���+\201\004\aE(�d"0]\201�\202\224=�<�"�F\022\035b�N^uͧ���m�i4���2\027\204.��`ܩ\202D�c\201���"...) at read_packet.c:355 packet = (struct lsh_string *) 0x401b1f60 closure = (struct read_packet *) 0x80af4b0 total = 32 #13 0x08055718 in do_buffered_read (s=0x809f288, fd=0x809eb48) at io.c:498 buffer = ( uint8_t *) 0xbfffaef0 "���;0�\035\213>l3\024\215+�\231�\020\204�\217_!\210�\003��\004�\210\213\005��\213\003'K�|�qOQ6�6.U\217K�\232\f�\v\+s<���:U3\001g\a�1z¨J\030֬%�\020%R\202�\006�\221\022dݡ9�F�\030-R\0236�\027�I����lK~vYh\200\005i\221#KXk�!hw�\225vB\177\203=/~�\036:\236w�\005�\035|0\215u.7�\223���+\201\004\aE(�d"0]\201�\202\224=�<�"�F\022\035b�N^uͧ���m�i4���2\027"... res = 0 #14 0x08054f5b in lsh_oop_fd_read_callback (s=0x8092788, fileno=0, event=OOP_READ, data=0x809eb48) at io.c:140 No locals. #15 0x4006e0a9 in oop_sys_run () from /usr/lib/liboop.so.3 No symbol table info available. #16 0x08055551 in io_run () at io.c:337 res = (void *) 0x0 #17 0x0804be6a in main (argc=4, argv=0xbffff1b4) at lshd.c:1170 options = (struct lshd_options *) 0x809c928 resources = (struct resource_list *) 0x8092548 keys = (struct alist *) 0x8092568 fds = (struct resource *) 0x809e980 r = {rlim_cur = 4294967295, rlim_max = 4294967295} (gdb)
/* server_gssapi.c * * GSSAPI authentication method */
/* lsh, an implementation of the ssh protocol * * Copyright (C) 2003 Simon Josefsson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#if HAVE_CONFIG_H #include "config.h" #endif
#include "charset.h" #include "format.h" #include "parse.h" #include "ssh.h" #include "server_userauth.h" #include "werror.h" #include "xalloc.h"
#include "server_gssapi.c.x"
#if WITH_GSS
#include <gss.h>
static struct lsh_string * format_userauth_gssapi_response(uint32_t len, uint8_t *oid) { return ssh_format("%c%s", SSH_MSG_USERAUTH_GSSAPI_RESPONSE, len, oid); }
static void display_status_1 (char *m, OM_uint32 code, int type) { OM_uint32 maj_stat, min_stat; gss_buffer_desc msg; OM_uint32 msg_ctx;
msg_ctx = 0; do { maj_stat = gss_display_status (&min_stat, code, type, GSS_C_NULL_OID, &msg_ctx, &msg); printf("GSS-API error %s: %s\n", m, (char *) msg.value); gss_release_buffer (&min_stat, &msg); } while (msg_ctx); }
static void display_status (char *msg, OM_uint32 maj_stat, OM_uint32 min_stat) { display_status_1 (msg, maj_stat, GSS_C_GSS_CODE); display_status_1 (msg, min_stat, GSS_C_MECH_CODE); }
struct lsh_user *user; struct command_continuation *cont;
/* GABA: (class (name gssapi_finish_handler) (super packet_handler) (vars (gssapi object gssapi_server_instance))) */
static void do_handle_gssapi_finish(struct packet_handler *s, struct ssh_connection *connection, struct lsh_string *packet) { CAST(gssapi_finish_handler, self, s); puts("ok"); /* Remember that a user was authenticated. */ self->gssapi->user = USER_LOOKUP(self->gssapi->db, self->gssapi->username, 1); connection->user = self->gssapi->user; COMMAND_RETURN(self->gssapi->cont, self->gssapi->user); }
static struct packet_handler * make_gssapi_finish_handler(struct gssapi_server_instance *gssapi) { NEW(gssapi_finish_handler, self); self->super.handler = do_handle_gssapi_finish; self->gssapi = gssapi;
return &self->super; }
/* GABA: (class (name gssapi_token_handler) (super packet_handler) (vars (gssapi object gssapi_server_instance))) */
static void do_handle_gssapi_token(struct packet_handler *s, struct ssh_connection *connection, struct lsh_string *packet) { CAST(gssapi_token_handler, self, s); OM_uint32 maj_stat; OM_uint32 min_stat; OM_uint32 retflags; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc inbuf, outbuf; gss_name_t client;
maj_stat = gss_acquire_cred (&min_stat, GSS_C_NO_NAME, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &cred, NULL, NULL); if (GSS_ERROR(maj_stat)) { display_status("acquire_cred", maj_stat, min_stat); /* XXX send SSH_MSG_USERAUTH_GSSAPI_ERROR */ PROTOCOL_ERROR(connection->e, "GSSAPI acquire_cred failed."); }
inbuf.value = packet->data + 1 + 4; inbuf.length = packet->length - 1 - 4; maj_stat = gss_accept_sec_context (&min_stat, &ctx, cred, &inbuf, GSS_C_NO_CHANNEL_BINDINGS, &client, GSS_C_NO_OID, &outbuf, &retflags, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { display_status("accept_sec_context", maj_stat, min_stat); /* XXX send SSH_MSG_USERAUTH_GSSAPI_ERROR */ PROTOCOL_ERROR(connection->e, "GSSAPI accept_sec_context failed."); }
if (maj_stat == GSS_S_COMPLETE) { connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] = &connection_unimplemented_handler; connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE] = make_gssapi_finish_handler(self->gssapi); C_WRITE(connection, ssh_format("%c%s", SSH_MSG_USERAUTH_GSSAPI_TOKEN, outbuf.length, outbuf.value)); return; } }
static struct packet_handler * make_gssapi_token_handler(struct gssapi_server_instance *gssapi) { NEW(gssapi_token_handler, self); self->super.handler = do_handle_gssapi_token; self->gssapi = gssapi;
return &self->super; }
/* GABA: (class (name userauth_gssapi) (super userauth) (vars (db object user_db))) */
/* GABA: (class (name gssapi_server_instance) (super userauth_gssapi) (vars (cont object command_continuation) (db object user_db) (user object lsh_user) (username object lsh_string))) */
static void do_authenticate(struct userauth *s, struct ssh_connection *connection UNUSED, struct lsh_string *username, uint32_t service UNUSED, struct simple_buffer *args, struct command_continuation *c, struct exception_handler *e) { CAST(userauth_gssapi, self, s);
NEW(gssapi_server_instance, gssapi);
int number_of_mechanisms;
username = utf8_to_local(username, 1, 1); if (!username) { PROTOCOL_ERROR(e, "Invalid utf8 in username."); return; } gssapi->username = username; gssapi->db = self->db; gssapi->cont = c;
if (parse_uint32(args, &number_of_mechanisms)) { int i; uint32_t len; uint8_t *oid;
printf("hepp %d\n", number_of_mechanisms);
for (i = 0; i < number_of_mechanisms; i++) if (!parse_string(args, &len, &oid)) goto fail;
connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] = make_gssapi_token_handler (gssapi); EXCEPTION_RAISE(e, make_userauth_special_exception (format_userauth_gssapi_response(len, oid), NULL)); return; } fail: /* Request was invalid */ lsh_string_free(username);
PROTOCOL_ERROR(e, "Invalid gssapi USERAUTH message."); }
struct userauth * make_userauth_gssapi(struct user_db *db) { NEW(userauth_gssapi, self); self->super.authenticate = do_authenticate; self->db = db;
return &self->super; }
#endif /* WITH_GSS */
Simon Josefsson jas@extundo.com writes:
I'm sorry for the length of this, if we should keep this off the list, please respond in private.
I prefer having development discussions on the public list, after all, I'm not the only one that's hacking lsh. There's also a (Swedish) conference "lsh (-) utvecklingsforum" in the KOM system at lyskom.lysator.liu.se, if you prefer that. It predates the mailinglist, and at least I and Pontus read it regularly.
It was rather straightforward, and I now have interop between lsh in server mode against OpenSSH (with the GSSAPI patches) in client mode. But. Once the authentication finished, lsh more or less crashes. I'm fairly certain this is because I don't understand the OOP stuff, and only have a few hours of experience with lsh in general.
I think I understand the problem. First, let me explain the connection_lock/connection_unlock thing. The point is to make sure that that during the processing of one userauth message, further messages that are received from the client are not processed, just queued (in lshd or in kernel socket buffers) for processing when finished with the current userauth message. Userauth messages need to be serialized in the server, while the client is allowed to send any number of userauth messages without waiting for replies.
This doesn't matter much for userauth methods which results in an answer right away, without returning to the main select loop. The main reason for it is password authentication with a helper program, as the userauth handler will then spawn a process, set an exit callback on the process, and return to the main select loop. Later the exit callback is invoked, it will pass a value or an exception back to the userauth code which then unlocks the connection.
The connection is locked in server_userauth:do_handle_userauth, and unlocked in the continuation handler do_userauth_continuation (invoked when userauth succeeds) and in do_exc_userauth_handler, for exceptions of type EXC_USERAUTH or EXC_USERAUTH_SPECIAL.
For all earlier userauth methods, the client sends a SSH_MSG_USERAUTH, and then the server replies with a success or failure or some special message like PUBLICKEY_OK. GSS-API seems a little different, due to the SSH_MSG_USERAUTH_GSSAPI_TOKEN and SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE which are sent by the client. The general server userauth code don't see these, so the connection isn't locked automatically, but the general userauth code still tries to unlock it when you invoke the continuation or exception handler.
I think the simplest way to solve the problem is to add calls to connection_lock at the start of the packet handlers you install.
That may not get things exactly right, one also needs to consider a client that doesn't send SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE as expected, but instead sends a new SSH_MSG_USERAUTH. Without having read the GSS-API spec, I guess that such client behaviour should either cancel the GSS-API exchange which is in progress, or raise a protocol error. Changes to the general server_userauth code may be needed to get that right, as there currently is no notion of a "subprotocol" being in progress, nor for cancelling such a subprotocol.
Finally, some minor comments on your code:
printf("GSS-API error %s: %s\n", m, (char *) msg.value);
The recommended way to print messages to the log is to use werror(), verbose() or debug(). So that printf could be replaced with
debug("GSS-API error %z: %z\n", m, (char *) msg.value);
struct lsh_user *user; struct command_continuation *cont;
Global variables must be avoided, they will confuse the gc unless special care is taken. But you have probably figured that out already, as you don't seem to use these globals anywhere.
/* GABA: (class (name gssapi_finish_handler) (super packet_handler) (vars (gssapi object gssapi_server_instance))) */
When possible, i.e. when there are no circular dependencies, I try to order classes so that if gssapi_finish_handler uses gssapi_server_instance, the latter class is defined first.
static void do_handle_gssapi_finish(struct packet_handler *s, struct ssh_connection *connection, struct lsh_string *packet) { CAST(gssapi_finish_handler, self, s); puts("ok"); /* Remember that a user was authenticated. */ self->gssapi->user = USER_LOOKUP(self->gssapi->db, self->gssapi->username, 1); connection->user = self->gssapi->user;
Any particular reason why you need to store the user in the gssapi object? Do you use the object after the userauthentication is finished?
You should probably check for USER_LOOKUP returning NULL (happens if the user doesn't exist or login is disabled).
static void do_handle_gssapi_token(struct packet_handler *s, struct ssh_connection *connection, struct lsh_string *packet) { CAST(gssapi_token_handler, self, s); OM_uint32 maj_stat; OM_uint32 min_stat; OM_uint32 retflags; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc inbuf, outbuf; gss_name_t client;
This is where an extra connection_lock call would go.
maj_stat = gss_acquire_cred (&min_stat, GSS_C_NO_NAME, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &cred, NULL, NULL);
Can gss_acquire_cred (or other gss-api functions) block, for example for contacting a kerberos server? Then the entire lshd server will block too. If that is a problem, we need either a non-blocking "native" interface to gss-api, or put the gss-api code in a separate process.
static void do_authenticate(struct userauth *s, struct ssh_connection *connection UNUSED,
^^^^^^^^^^^^^^^^^ ...
connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] =
This seems inconsistent...
Thanks for the nice work! Regards, /Niels
On Mon, Jun 30, 2003 at 11:29:09AM +0200, Niels Möller wrote:
Simon Josefsson jas@extundo.com writes:
I'm sorry for the length of this, if we should keep this off the list, please respond in private.
I prefer having development discussions on the public list, after all, I'm not the only one that's hacking lsh.
Yes, others are watching (and learning) as well. :-)
I'd very much like to see GSSAPI support in lsh.
/fc
(Someone on this list appear to be using an annoying and severely miss-configured spam prevention systems called COSI, or something, that sends mail to everyone that posts to the list asking them to respond with cookies, a cookie that goes to the original author. If people have to use such systems at all, please at least configure them properly, and have them include information on whom the person requesting the cookie is so I can consciously ignore it.)
nisse@lysator.liu.se (Niels Möller) writes:
I think I understand the problem. First, let me explain the connection_lock/connection_unlock thing. The point is to make sure that that during the processing of one userauth message, further messages that are received from the client are not processed, just queued (in lshd or in kernel socket buffers) for processing when finished with the current userauth message. Userauth messages need to be serialized in the server, while the client is allowed to send any number of userauth messages without waiting for replies.
This doesn't matter much for userauth methods which results in an answer right away, without returning to the main select loop. The main reason for it is password authentication with a helper program, as the userauth handler will then spawn a process, set an exit callback on the process, and return to the main select loop. Later the exit callback is invoked, it will pass a value or an exception back to the userauth code which then unlocks the connection.
The connection is locked in server_userauth:do_handle_userauth, and unlocked in the continuation handler do_userauth_continuation (invoked when userauth succeeds) and in do_exc_userauth_handler, for exceptions of type EXC_USERAUTH or EXC_USERAUTH_SPECIAL.
For all earlier userauth methods, the client sends a SSH_MSG_USERAUTH, and then the server replies with a success or failure or some special message like PUBLICKEY_OK. GSS-API seems a little different, due to the SSH_MSG_USERAUTH_GSSAPI_TOKEN and SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE which are sent by the client. The general server userauth code don't see these, so the connection isn't locked automatically, but the general userauth code still tries to unlock it when you invoke the continuation or exception handler.
I think the simplest way to solve the problem is to add calls to connection_lock at the start of the packet handlers you install.
Adding connection_lock(connection) to do_handle_gssapi_finish (i.e., before COMMAND_RETURN is invoked) solves the problem, and authentication succeeds and the login proceeds. However, if I add the lock to do_handle_gssapi_token, lsh stalls after responding with the GSSAPI_TOKEN to the client. It seems to never get the EXCHANGE_COMPLETE from the client. For now I'll just ignore this problem since it works, and I don't understand how to improve it.
That may not get things exactly right, one also needs to consider a client that doesn't send SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE as expected, but instead sends a new SSH_MSG_USERAUTH. Without having read the GSS-API spec, I guess that such client behaviour should either cancel the GSS-API exchange which is in progress, or raise a protocol error. Changes to the general server_userauth code may be needed to get that right, as there currently is no notion of a "subprotocol" being in progress, nor for cancelling such a subprotocol.
It should restart authentication, and the previous authentication attempt should be forgotten. I think this will work without much extra logic, since the newly invoked userauth_gssapi code installs fresh handlers for the GSSAPI messages every time. Probably, they could kill any existing handler to make sure resources are deallocated.
Thanks for the explanation.
maj_stat = gss_acquire_cred (&min_stat, GSS_C_NO_NAME, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &cred, NULL, NULL);
Can gss_acquire_cred (or other gss-api functions) block, for example for contacting a kerberos server? Then the entire lshd server will block too. If that is a problem, we need either a non-blocking "native" interface to gss-api, or put the gss-api code in a separate process.
GSS is generic, so anything can occur, but the specification says the function is NOT intended to do a network logon, and that if the operation takes time it can be delayed until gss_accept_sec_context(). For Kerberos 5, GSS in server never does any network communication, it just reads a secret key from a file and parse and generate tokens using it. I wouldn't worry about this until someone has a problem. What is the worst problem this could cause anyway? I can only think of the lsh server core being delayed when responding to the client, but the client should kind of expect this anyway, when it requests a GSS mechanism that takes a very long time to finish.
Thanks again, Simon
Simon Josefsson jas@extundo.com writes:
Adding connection_lock(connection) to do_handle_gssapi_finish (i.e., before COMMAND_RETURN is invoked) solves the problem, and authentication succeeds and the login proceeds. However, if I add the lock to do_handle_gssapi_token, lsh stalls after responding with the GSSAPI_TOKEN to the client.
Hmm, that's natural, given that do_handle_gssapi_token uses C_WRITE to write the response, not EXC_USERAUTH_SPECIAL. The automatic locking and unlocking happens only when the flow of control passes through the general userauth code, which it doesn't do here.
That may not get things exactly right, one also needs to consider a client that doesn't send SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE as expected, but instead sends a new SSH_MSG_USERAUTH. Without having read the GSS-API spec, I guess that such client behaviour should either cancel the GSS-API exchange which is in progress, or raise a protocol error. Changes to the general server_userauth code may be needed to get that right, as there currently is no notion of a "subprotocol" being in progress, nor for cancelling such a subprotocol.
It should restart authentication, and the previous authentication attempt should be forgotten. I think this will work without much extra logic, since the newly invoked userauth_gssapi code installs fresh handlers for the GSSAPI messages every time. Probably, they could kill any existing handler to make sure resources are deallocated.
I suspect some extra logic is needed, but perhaps not much. Consider a client sending
SSH_MSG_USERAUTH "gssapi" (starting a gssapi "session") SSH_MSG_USERAUTH "none" (restarting authentication) SSH_MSG_USERAUTH_GSSAPI_TOKEN
Then the client should get a protocol error, but it might make contact with your old handler which is still installed. It might be simple to fix, by having the handler for SSH_MSG_USERAUTH reset all handlers for messages in the userauth range.
Can gss_acquire_cred (or other gss-api functions) block, for example for contacting a kerberos server? Then the entire lshd server will block too.
What is the worst problem this could cause anyway? I can only think of the lsh server core being delayed when responding to the client, but the client should kind of expect this anyway, when it requests a GSS mechanism that takes a very long time to finish.
What will happen is that the lshd server will block and stop responding on *all* connections, for all users. So it's a denial of service attack on the other users of the system.
Regards, /Niels
I have put up a web page at
http://josefsson.org/gss/gss-lsh.html
with some information and the current patch. It works fairly well, but some minor issues in the specification probably doesn't work.
I hope I have fixed the problems mentioned earlier. Let me know what you think. I'm going to test it with other GSS libraries now.
ObWarning: I didn't review it for logical security flaws. There could be major holes.
I suspect some extra logic is needed, but perhaps not much. Consider a client sending
SSH_MSG_USERAUTH "gssapi" (starting a gssapi "session") SSH_MSG_USERAUTH "none" (restarting authentication) SSH_MSG_USERAUTH_GSSAPI_TOKEN
Then the client should get a protocol error, but it might make contact with your old handler which is still installed. It might be simple to fix, by having the handler for SSH_MSG_USERAUTH reset all handlers for messages in the userauth range.
Yes, sounds reasonable.
One additional thing would be required: how do I make the GC invoke a function (do_gc_gssapi() in the patch above) when a gssapi_server_instance class instance is gc'ed? The GSS library allocates some resources that should be deallocated if an authentication attempt is aborted.
Can gss_acquire_cred (or other gss-api functions) block, for example for contacting a kerberos server? Then the entire lshd server will block too.
What is the worst problem this could cause anyway? I can only think of the lsh server core being delayed when responding to the client, but the client should kind of expect this anyway, when it requests a GSS mechanism that takes a very long time to finish.
What will happen is that the lshd server will block and stop responding on *all* connections, for all users. So it's a denial of service attack on the other users of the system.
Oops, I didn't think of this. OK, it seems fairly clear that GSS should be invoked from a different process, which communicate with the lsh core using some protocol, then. Still, since kerberos 5 GSS mechanism is fast, this is probably not a show-stopper. I think I'll leave this as an exercise.
Thanks for the guidance, Simon
Sorry for the spamming, but http://josefsson.org/gss/gss-lsh.html now contain a patch that works with the GSS library in Heimdal and MIT Kerberos 5 too, if you configure with --enable-gss=k5.
My next goal is client mode, but I won't start until people have tried the server part. Meanwhile I'll focus on my own GSS library, which was the reason I got here in the first place. Incidentally, I hardly had to modify it to make it work in LSH, so I'll have to find some better way to test it.
Simon Josefsson jas@extundo.com writes:
Sorry for the spamming, but http://josefsson.org/gss/gss-lsh.html now contain a patch that works with the GSS library in Heimdal and MIT Kerberos 5 too, if you configure with --enable-gss=k5.
Client mode is also supported now, tested with GSS/Heimdal/MIT. Still interested in feedback.
Btw, has anyone got the OpenSSH GSSAPI user authentication to work? NB, not the GSSAPI key exchange. Add "gssapikeyexchange no" to your sshd_config and test if you still can log in using GSSAPI with the OpenSSH client. It didn't work here, so I suspect OpenSSH is buggy.
I've updated http://josefsson.org/gss/gss-lsh.html to reflect current status. LSH-GSS talks to latest OpenSSH properly, and I'm told the vsh client can talk to lshd (reverse direction not yet tested). There hasn't been any code changes since the last release.
Hi,
in addition to Niels comments, here are some unstructured thoughts - I do realize that this is work in progress, but I want to point it out so I don't forget. Also consider this food for thought, I may be wrong about things.
static void do_handle_gssapi_token(struct packet_handler *s, struct ssh_connection *connection, struct lsh_string *packet) { CAST(gssapi_token_handler, self, s); OM_uint32 maj_stat; OM_uint32 min_stat; OM_uint32 retflags; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc inbuf, outbuf; gss_name_t client;
maj_stat = gss_acquire_cred (&min_stat, GSS_C_NO_NAME, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &cred, NULL, NULL); if (GSS_ERROR(maj_stat)) { display_status("acquire_cred", maj_stat, min_stat); /* XXX send SSH_MSG_USERAUTH_GSSAPI_ERROR */ PROTOCOL_ERROR(connection->e, "GSSAPI acquire_cred failed."); }
I think you should reset connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] here and have connection->explicit returns after PROTOCOL_ERRORS.
inbuf.value = packet->data + 1 + 4; inbuf.length = packet->length - 1 - 4; maj_stat = gss_accept_sec_context (&min_stat, &ctx, cred, &inbuf, GSS_C_NO_CHANNEL_BINDINGS, &client, GSS_C_NO_OID, &outbuf, &retflags, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { display_status("accept_sec_context", maj_stat, min_stat); /* XXX send SSH_MSG_USERAUTH_GSSAPI_ERROR */ PROTOCOL_ERROR(connection->e, "GSSAPI accept_sec_context failed."); }
if (maj_stat == GSS_S_COMPLETE) { connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] = &connection_unimplemented_handler; connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE] = make_gssapi_finish_handler(self->gssapi); C_WRITE(connection, ssh_format("%c%s", SSH_MSG_USERAUTH_GSSAPI_TOKEN, outbuf.length, outbuf.value)); return; } }
Missing case maj_stat == GSS_S_CONTINUE_NEEDED? It's probably a good idea to reset both connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_TOKEN] and connection->dispatch[SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE] at the beginning and set the correct one before returning if all goes well.
if (parse_uint32(args, &number_of_mechanisms)) { int i; uint32_t len; uint8_t *oid;
printf("hepp %d\n", number_of_mechanisms); for (i = 0; i < number_of_mechanisms; i++)
if (!parse_string(args, &len, &oid)) goto fail;
I assume there will be a check if the given mechanism is available here?
If I'm not missing something, the current implementation would allow an attacker to request authentication as user foo and use GSS to authenticate herself as bar, and gain access as foo, which would be a bad thing, I assume GSS can be told to only accept authentication for a given user or that one can check after authentication who was authenticated?
Anyway, very nice work, having GSS support seems to be a Good Thing. Kudos to you.
/Pontus
Pontus Skoeld pont@soua.net writes:
Hi,
in addition to Niels comments, here are some unstructured thoughts - I do realize that this is work in progress, but I want to point it out so I don't forget. Also consider this food for thought, I may be wrong about things.
Hello, and thanks for the suggestions. If you could look at the code again, when I believe I'm satisfied with it, it would be even better. :-)
I assume there will be a check if the given mechanism is available here?
Yup.
If I'm not missing something, the current implementation would allow an attacker to request authentication as user foo and use GSS to authenticate herself as bar, and gain access as foo, which would be a bad thing
Yup.
I assume GSS can be told to only accept authentication for a given user or that one can check after authentication who was authenticated?
You can extract the identity of the user who was authenticated, and then carry out the authorization procedure. Is there an authorization infrastructure in lsh? If not, one should probably simply try to log in as the authenticated GSSAPI identity instead of the requested username (the username can be empty according to the GSS-SSH specification).
Thanks, Simon
Simon Josefsson jas@extundo.com writes:
You can extract the identity of the user who was authenticated, and then carry out the authorization procedure. Is there an authorization infrastructure in lsh?
I'm not sure exactly what you're asking, but my guess is "no".
Userauth methods return user objects, which implies that the client should be allowed to do most anything that user is allowed to do. These objects are usually looked up using the user_db abstraction.
I'd like to hook spki authorization into lsh, but that's nothing that exists today. The simplest way to go about that is to put an spki "tag" into the user object before it's returned from a userauth method. Tags could be things like
(tag (login ...)) (tag (command foo)) (tag (sftp read (* prefix "/home/nisse/pub"))
/Niels
nisse@lysator.liu.se (Niels Möller) writes:
Simon Josefsson jas@extundo.com writes:
You can extract the identity of the user who was authenticated, and then carry out the authorization procedure. Is there an authorization infrastructure in lsh?
I'm not sure exactly what you're asking, but my guess is "no".
Userauth methods return user objects, which implies that the client should be allowed to do most anything that user is allowed to do. These objects are usually looked up using the user_db abstraction.
Right, what I was looking for was something similar to
connection->user = USER_LOOKUP(self->gssapi->db, authid, authzid);
where authid was the GSS authenticated user (e.g., "jas") and authzid the username received from the first USERAUTH_REQUEST (e.g., "root"). The function would also check if jas was entitled to log on as root. (Some Kerberos implementation have a similar function (kuserok), but I always found it a gross design violation, as Kerberos is not an authorization mechanism.)
Simon Josefsson jas@extundo.com writes:
Right, what I was looking for was something similar to
connection->user = USER_LOOKUP(self->gssapi->db, authid, authzid);
where authid was the GSS authenticated user (e.g., "jas") and authzid the username received from the first USERAUTH_REQUEST (e.g., "root").
lshd cares only about the username provided in the USERAUTH_REQUEST, and the passwd-information (uid, loginshell, etc) in the user object returned by the userauth method. These are generally expected to match.
For the case of a user, "jas", logging in as root (for kerberos, that means that "jas" is listed in a file in root's home directory, IIRC), I would expect the USERAUTH_REQUEST to request login for "root", and the information that you want to use tickets belonging to "jas" should be somewhere inside the gss-api or kerberos messages, and then I can view it as an internal detail of the gss-api mechanism. Checking that "jas" is allowed to login as root should be the resposibility of the gss-api userauth method.
I think it's possible for a userauth method to turn a USERAUTH_REQUEST for the user "jas" into a user object that represents "root", but that seems fishy to me. I prefer to not have the username -> user mapping depend on the particular userauth method that is used.
Regards, /Niels
nisse@lysator.liu.se (Niels Möller) writes:
Simon Josefsson jas@extundo.com writes:
Right, what I was looking for was something similar to
connection->user = USER_LOOKUP(self->gssapi->db, authid, authzid);
where authid was the GSS authenticated user (e.g., "jas") and authzid the username received from the first USERAUTH_REQUEST (e.g., "root").
lshd cares only about the username provided in the USERAUTH_REQUEST, and the passwd-information (uid, loginshell, etc) in the user object returned by the userauth method. These are generally expected to match.
One problem is that the GSSAPI document allows the field to be empty, to provide anonymous usage. Fortunately, OpenSSH doesn't support this though.
For the case of a user, "jas", logging in as root (for kerberos, that means that "jas" is listed in a file in root's home directory, IIRC), I would expect the USERAUTH_REQUEST to request login for "root", and the information that you want to use tickets belonging to "jas" should be somewhere inside the gss-api or kerberos messages, and then I can view it as an internal detail of the gss-api mechanism. Checking that "jas" is allowed to login as root should be the resposibility of the gss-api userauth method.
OK. This is almost how it works now. Only Heimdal/MIT support it, via the kuserok function. When GSS is used, one is only allowed to log on as the authenticated username.
Thanks, Simon