1 /* server.c --- Experimental SASL mechanism KERBEROS_V5, server side.
2  * Copyright (C) 2003-2021 Simon Josefsson
3  *
4  * This file is part of GNU SASL Library.
5  *
6  * GNU SASL Library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GNU SASL Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GNU SASL Library; if not, write to the Free
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * NB!  Shishi is licensed under GPL, so linking GSASL with it require
22  * that you follow the GPL for GSASL as well.
23  *
24  */
25 
26 #include "kerberos_v5.h"
27 
28 #include "shared.h"
29 
30 struct _Gsasl_kerberos_v5_server_state
31 {
32   int firststep;
33   Shishi *sh;
34   char serverhello[BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN];
35   char *random;
36   int serverqops;
37   uint32_t servermaxbuf;
38   int clientqop;
39   int clientmutual;
40   uint32_t clientmaxbuf;
41   char *username;
42   char *userrealm;
43   char *serverrealm;
44   char *serverservice;
45   char *serverhostname;
46   char *password;
47   Shishi_key *userkey;		/* user's key derived with string2key */
48   Shishi_key *sessionkey;	/* shared between client and server */
49   Shishi_key *sessiontktkey;	/* known only by server */
50   Shishi_ap *ap;
51   Shishi_as *as;
52   Shishi_safe *safe;
53 };
54 
55 int
_gsasl_kerberos_v5_server_init(Gsasl_ctx * ctx)56 _gsasl_kerberos_v5_server_init (Gsasl_ctx * ctx)
57 {
58   if (!shishi_check_version (SHISHI_VERSION))
59     return GSASL_UNKNOWN_MECHANISM;
60 
61   return GSASL_OK;
62 }
63 
64 int
_gsasl_kerberos_v5_server_start(Gsasl_session * sctx,void ** mech_data)65 _gsasl_kerberos_v5_server_start (Gsasl_session * sctx, void **mech_data)
66 {
67   struct _Gsasl_kerberos_v5_server_state *state;
68   int err;
69 
70   state = malloc (sizeof (*state));
71   if (state == NULL)
72     return GSASL_MALLOC_ERROR;
73   memset (state, 0, sizeof (*state));
74 
75   state->random = (char *) malloc (RANDOM_LEN);
76   if (state->random == NULL)
77     return GSASL_MALLOC_ERROR;
78 
79   err = shishi_init_server (&state->sh);
80   if (err)
81     return GSASL_KERBEROS_V5_INIT_ERROR;
82 
83   err = shishi_randomize (state->sh, state->random, RANDOM_LEN);
84   if (err)
85     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
86 
87   /* This can be pretty much anything, the client will never have it. */
88   err = shishi_key_random (state->sh, SHISHI_AES256_CTS_HMAC_SHA1_96,
89 			   &state->sessiontktkey);
90   if (err)
91     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
92 
93   err = shishi_as (state->sh, &state->as);
94   if (err)
95     return GSASL_KERBEROS_V5_INTERNAL_ERROR;
96 
97   state->firststep = 1;
98   state->serverqops = GSASL_QOP_AUTH | GSASL_QOP_AUTH_INT;
99 
100   *mech_data = state;
101 
102   return GSASL_OK;
103 }
104 
105 int
_gsasl_kerberos_v5_server_step(Gsasl_session * sctx,void * mech_data,const char * input,size_t input_len,char * output,size_t * output_len)106 _gsasl_kerberos_v5_server_step (Gsasl_session * sctx,
107 				void *mech_data,
108 				const char *input,
109 				size_t input_len,
110 				char *output, size_t *output_len)
111 {
112   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
113   Gsasl_server_callback_realm cb_realm;
114   Gsasl_server_callback_qop cb_qop;
115   Gsasl_server_callback_maxbuf cb_maxbuf;
116   Gsasl_server_callback_cipher cb_cipher;
117   Gsasl_server_callback_retrieve cb_retrieve;
118   Gsasl_server_callback_service cb_service;
119   unsigned char buf[BUFSIZ];
120   size_t buflen;
121   Gsasl_ctx *ctx;
122   ASN1_TYPE asn1;
123   int err;
124 
125   ctx = gsasl_server_ctx_get (sctx);
126   if (ctx == NULL)
127     return GSASL_CANNOT_GET_CTX;
128 
129   cb_realm = gsasl_server_callback_realm_get (ctx);
130   cb_qop = gsasl_server_callback_qop_get (ctx);
131   cb_maxbuf = gsasl_server_callback_maxbuf_get (ctx);
132   cb_retrieve = gsasl_server_callback_retrieve_get (ctx);
133   cb_service = gsasl_server_callback_service_get (ctx);
134   if (cb_service == NULL)
135     return GSASL_NEED_SERVER_SERVICE_CALLBACK;
136 
137   if (state->firststep)
138     {
139       uint32_t tmp;
140       unsigned char *p;
141 
142       /*
143        * The initial server packet should contain one octet containing
144        * a bit mask of supported security layers, four octets
145        * indicating the maximum cipher-text buffer size the server is
146        * able to receive (or 0 if no security layers are supported) in
147        * network byte order, and then 16 octets containing random data
148        * (see [4] on how random data might be generated).
149        *
150        * The security layers and their corresponding bit-masks are as
151        * follows:
152        *
153        *       Bit 0 No security layer
154        *       Bit 1 Integrity (KRB-SAFE) protection
155        *       Bit 2 Privacy (KRB-PRIV) protection
156        *       Bit 3 Mutual authentication is required (AP option MUTUAL-
157        *             REQUIRED must also be present).
158        *
159        * Other bit-masks may be defined in the future; bits which are
160        * not understood must be negotiated off.
161        *
162        */
163       if (output && *output_len < BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN)
164 	return GSASL_TOO_SMALL_BUFFER;
165 
166       p = &state->serverhello[0];
167 
168       if (cb_qop)
169 	state->serverqops = cb_qop (sctx);
170       *p = 0;
171       if (state->serverqops & GSASL_QOP_AUTH)
172 	*p |= GSASL_QOP_AUTH;
173       if (state->serverqops & GSASL_QOP_AUTH_INT)
174 	*p |= GSASL_QOP_AUTH_INT;
175       if (state->serverqops & GSASL_QOP_AUTH_CONF)
176 	*p |= GSASL_QOP_AUTH_CONF;
177       /* XXX we always require mutual authentication for now */
178       *p |= MUTUAL;
179 
180       if (!(state->serverqops & ~GSASL_QOP_AUTH))
181 	state->servermaxbuf = 0;
182       else if (cb_maxbuf)
183 	state->servermaxbuf = cb_maxbuf (sctx);
184       else
185 	state->servermaxbuf = MAXBUF_DEFAULT;
186 
187       tmp = htonl (state->servermaxbuf);
188       memcpy (&state->serverhello[BITMAP_LEN], &tmp, MAXBUF_LEN);
189       memcpy (&state->serverhello[BITMAP_LEN + MAXBUF_LEN],
190 	      state->random, RANDOM_LEN);
191 
192       if (output)
193 	memcpy (output, state->serverhello, SERVER_HELLO_LEN);
194       *output_len = BITMAP_LEN + MAXBUF_LEN + RANDOM_LEN;
195 
196       state->firststep = 0;
197 
198       return GSASL_NEEDS_MORE;
199     }
200 
201   if (cb_retrieve)
202     {
203       /* Non-infrastructure mode */
204 
205       if (*output_len < 2048)
206 	return GSASL_TOO_SMALL_BUFFER;
207 
208       if (shishi_as_req_der_set (state->as, input, input_len) == SHISHI_OK)
209 	{
210 	  Shishi_tkt *tkt;
211 	  int etype, i;
212 
213 	  tkt = shishi_as_tkt (state->as);
214 	  if (!tkt)
215 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
216 
217 	  i = 1;
218 	  do
219 	    {
220 	      err = shishi_kdcreq_etype (state->sh,
221 					 shishi_as_req (state->as),
222 					 &etype, i);
223 	      if (err == SHISHI_OK && shishi_cipher_supported_p (etype))
224 		break;
225 	    }
226 	  while (err == SHISHI_OK);
227 	  if (err != SHISHI_OK)
228 	    return err;
229 
230 	  /* XXX use a "preferred server kdc etype" from shishi instead? */
231 	  err = shishi_key_random (state->sh, etype, &state->sessionkey);
232 	  if (err)
233 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
234 
235 	  err = shishi_tkt_key_set (tkt, state->sessionkey);
236 	  if (err)
237 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
238 
239 	  buflen = sizeof (buf) - 1;
240 	  err = shishi_kdcreq_cname_get (state->sh,
241 					 shishi_as_req (state->as),
242 					 buf, &buflen);
243 	  if (err != SHISHI_OK)
244 	    return err;
245 	  buf[buflen] = '\0';
246 	  state->username = strdup (buf);
247 
248 	  buflen = sizeof (buf) - 1;
249 	  err = shishi_kdcreq_realm_get (state->sh,
250 					 shishi_as_req (state->as),
251 					 buf, &buflen);
252 	  if (err != SHISHI_OK)
253 	    return err;
254 	  buf[buflen] = '\0';
255 	  state->userrealm = strdup (buf);
256 
257 	  buflen = sizeof (buf) - 1;
258 	  err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
259 			     NULL, &buflen);
260 	  if (err != GSASL_OK)
261 	    return err;
262 
263 	  state->password = malloc (buflen + 1);
264 	  if (state->password == NULL)
265 	    return GSASL_MALLOC_ERROR;
266 
267 	  err = cb_retrieve (sctx, state->username, NULL, state->userrealm,
268 			     state->password, &buflen);
269 	  if (err != GSASL_OK)
270 	    return err;
271 	  state->password[buflen] = '\0';
272 
273 	  buflen = sizeof (buf) - 1;
274 	  if (cb_realm)
275 	    {
276 	      err = cb_realm (sctx, buf, &buflen, 0);
277 	      if (err != GSASL_OK)
278 		return err;
279 	    }
280 	  else
281 	    buflen = 0;
282 	  buf[buflen] = '\0';
283 	  state->serverrealm = strdup (buf);
284 
285 	  buflen = sizeof (buf) - 1;
286 	  err = cb_service (sctx, buf, &buflen, NULL, NULL);
287 	  if (err != GSASL_OK)
288 	    return err;
289 	  buf[buflen] = '\0';
290 	  state->serverservice = strdup (buf);
291 
292 	  buflen = sizeof (buf) - 1;
293 	  err = cb_service (sctx, NULL, NULL, buf, &buflen);
294 	  if (err != GSASL_OK)
295 	    return err;
296 	  buf[buflen] = '\0';
297 	  state->serverhostname = strdup (buf);
298 
299 	  /* XXX do some checking on realm and server name?  Right now
300 	     we simply doesn't care about what client requested and
301 	     return a ticket for this server.  This is bad. */
302 
303 	  err = shishi_tkt_clientrealm_set (tkt, state->userrealm,
304 					    state->username);
305 	  if (err)
306 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
307 
308 	  {
309 	    char *p;
310 	    p = malloc (strlen (state->serverservice) + strlen ("/") +
311 			strlen (state->serverhostname) + 1);
312 	    if (p == NULL)
313 	      return GSASL_MALLOC_ERROR;
314 	    sprintf (p, "%s/%s", state->serverservice, state->serverhostname);
315 	    err = shishi_tkt_serverrealm_set (tkt, state->serverrealm, p);
316 	    free (p);
317 	    if (err)
318 	      return GSASL_KERBEROS_V5_INTERNAL_ERROR;
319 	  }
320 
321 	  buflen = sizeof (buf);
322 	  err = shishi_as_derive_salt (state->sh,
323 				       shishi_as_req (state->as),
324 				       shishi_as_rep (state->as),
325 				       buf, &buflen);
326 	  if (err != SHISHI_OK)
327 	    return err;
328 
329 	  err = shishi_key_from_string (state->sh,
330 					etype,
331 					state->password,
332 					strlen (state->password),
333 					buf, buflen, NULL, &state->userkey);
334 	  if (err != SHISHI_OK)
335 	    return err;
336 
337 	  err = shishi_tkt_build (tkt, state->sessiontktkey);
338 	  if (err)
339 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
340 
341 	  err = shishi_as_rep_build (state->as, state->userkey);
342 	  if (err)
343 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
344 
345 #if DEBUG
346 	  shishi_kdcreq_print (state->sh, stderr, shishi_as_req (state->as));
347 	  shishi_encticketpart_print (state->sh, stderr,
348 				      shishi_tkt_encticketpart (tkt));
349 	  shishi_ticket_print (state->sh, stderr, shishi_tkt_ticket (tkt));
350 	  shishi_enckdcreppart_print (state->sh, stderr,
351 				      shishi_tkt_enckdcreppart (state->as));
352 	  shishi_kdcrep_print (state->sh, stderr, shishi_as_rep (state->as));
353 #endif
354 
355 	  err = shishi_as_rep_der (state->as, output, output_len);
356 	  if (err)
357 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
358 
359 	  return GSASL_NEEDS_MORE;
360 	}
361       else if ((asn1 = shishi_der2asn1_apreq (state->sh, input, input_len)))
362 	{
363 	  int adtype;
364 
365 	  err = shishi_ap (state->sh, &state->ap);
366 	  if (err)
367 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
368 
369 	  shishi_ap_req_set (state->ap, asn1);
370 
371 	  err = shishi_ap_req_process (state->ap, state->sessiontktkey);
372 	  if (err)
373 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
374 
375 #if DEBUG
376 	  shishi_apreq_print (state->sh, stderr, shishi_ap_req (state->ap));
377 	  shishi_ticket_print (state->sh, stderr,
378 			       shishi_tkt_ticket (shishi_ap_tkt (state->ap)));
379 	  shishi_authenticator_print (state->sh, stderr,
380 				      shishi_ap_authenticator (state->ap));
381 #endif
382 
383 	  buflen = sizeof (buf);
384 	  err = shishi_authenticator_authorizationdata
385 	    (state->sh, shishi_ap_authenticator (state->ap),
386 	     &adtype, buf, &buflen, 1);
387 	  if (err)
388 	    return GSASL_KERBEROS_V5_INTERNAL_ERROR;
389 
390 	  if (adtype != 0xFF /* -1 in one-complements form */  ||
391 	      buflen < CLIENT_HELLO_LEN + SERVER_HELLO_LEN)
392 	    return GSASL_AUTHENTICATION_ERROR;
393 
394 	  {
395 	    unsigned char clientbitmap;
396 
397 	    memcpy (&clientbitmap, &buf[0], BITMAP_LEN);
398 	    state->clientqop = 0;
399 	    if (clientbitmap & GSASL_QOP_AUTH)
400 	      state->clientqop |= GSASL_QOP_AUTH;
401 	    if (clientbitmap & GSASL_QOP_AUTH_INT)
402 	      state->clientqop |= GSASL_QOP_AUTH_INT;
403 	    if (clientbitmap & GSASL_QOP_AUTH_CONF)
404 	      state->clientqop |= GSASL_QOP_AUTH_CONF;
405 	    if (clientbitmap & MUTUAL)
406 	      state->clientmutual = 1;
407 	  }
408 	  memcpy (&state->clientmaxbuf, &input[BITMAP_LEN], MAXBUF_LEN);
409 	  state->clientmaxbuf = ntohl (state->clientmaxbuf);
410 
411 	  if (!(state->clientqop & state->serverqops))
412 	    return GSASL_AUTHENTICATION_ERROR;
413 
414 	  /* XXX check clientmaxbuf too */
415 
416 	  if (memcmp (&buf[CLIENT_HELLO_LEN],
417 		      state->serverhello, SERVER_HELLO_LEN) != 0)
418 	    return GSASL_AUTHENTICATION_ERROR;
419 
420 	  {
421 	    char cksum[BUFSIZ];
422 	    int cksumlen;
423 	    int cksumtype;
424 	    Shishi_key *key;
425 
426 	    key = shishi_tkt_key (shishi_as_tkt (state->as));
427 	    cksumtype =
428 	      shishi_cipher_defaultcksumtype (shishi_key_type (key));
429 	    cksumlen = sizeof (cksum);
430 	    err = shishi_checksum (state->sh, key,
431 				   SHISHI_KEYUSAGE_APREQ_AUTHENTICATOR_CKSUM,
432 				   cksumtype, buf, buflen, cksum, &cksumlen);
433 	    if (err != SHISHI_OK)
434 	      return GSASL_KERBEROS_V5_INTERNAL_ERROR;
435 
436 	    buflen = sizeof (buf);
437 	    err = shishi_authenticator_cksum
438 	      (state->sh,
439 	       shishi_ap_authenticator (state->ap), &cksumtype, buf, &buflen);
440 	    if (err != SHISHI_OK)
441 	      return GSASL_KERBEROS_V5_INTERNAL_ERROR;
442 
443 	    if (buflen != cksumlen || memcmp (buf, cksum, buflen) != 0)
444 	      return GSASL_AUTHENTICATION_ERROR;
445 	  }
446 
447 	  /* XXX use authorization_id */
448 
449 	  if (state->clientmutual)
450 	    {
451 	      err = shishi_ap_rep_build (state->ap);
452 	      if (err)
453 		return GSASL_KERBEROS_V5_INTERNAL_ERROR;
454 
455 	      err = shishi_ap_rep_der (state->ap, output, output_len);
456 	      if (err)
457 		return GSASL_KERBEROS_V5_INTERNAL_ERROR;
458 	    }
459 	  else
460 	    *output_len = 0;
461 
462 	  return GSASL_OK;
463 	}
464     }
465   else
466     {
467       /* XXX Currently we only handle AS-REQ and AP-REQ in
468          non-infrastructure mode.  Supporting infrastructure mode is
469          simple, just send the AS-REQ to the KDC and wait for AS-REP
470          instead of creating AS-REP locally.
471 
472          We should probably have a callback to decide policy:
473          1) non-infrastructure mode (NIM) only
474          2) infrastructure mode (IM) only
475          3) proxied infrastructure mode (PIM) only
476          4) NIM with fallback to IM (useful for local server overrides)
477          5) IM with fallback to NIM (useful for admins if KDC is offline)
478          6) ...etc with PIM too
479        */
480       return GSASL_NEED_SERVER_RETRIEVE_CALLBACK;
481     }
482 
483   *output_len = 0;
484   return GSASL_NEEDS_MORE;
485 }
486 
487 int
_gsasl_kerberos_v5_server_encode(Gsasl_session * sctx,void * mech_data,const char * input,size_t input_len,char * output,size_t * output_len)488 _gsasl_kerberos_v5_server_encode (Gsasl_session * sctx,
489 				  void *mech_data,
490 				  const char *input,
491 				  size_t input_len,
492 				  char *output, size_t *output_len)
493 {
494   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
495   int res;
496 
497   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
498     {
499       return GSASL_INTEGRITY_ERROR;
500     }
501   else if (state && state->sessionkey
502 	   && state->clientqop & GSASL_QOP_AUTH_INT)
503     {
504       res = shishi_safe (state->sh, &state->safe);
505       if (res != SHISHI_OK)
506 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
507 
508       res = shishi_safe_set_user_data (state->sh,
509 				       shishi_safe_safe (state->safe),
510 				       input, input_len);
511       if (res != SHISHI_OK)
512 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
513 
514       res = shishi_safe_build (state->safe, state->sessionkey);
515       if (res != SHISHI_OK)
516 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
517 
518       res = shishi_safe_safe_der (state->safe, output, output_len);
519       if (res != SHISHI_OK)
520 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
521     }
522   else
523     {
524       *output_len = input_len;
525       *output = malloc (input_len);
526       if (!*output)
527 	return GSASL_MALLOC_ERROR;
528       memcpy (*output, input, input_len);
529     }
530 
531   return GSASL_OK;
532 }
533 
534 int
_gsasl_kerberos_v5_server_decode(Gsasl_session * sctx,void * mech_data,const char * input,size_t input_len,char * output,size_t * output_len)535 _gsasl_kerberos_v5_server_decode (Gsasl_session * sctx,
536 				  void *mech_data,
537 				  const char *input,
538 				  size_t input_len,
539 				  char *output, size_t *output_len)
540 {
541   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
542   int res;
543 
544   if (state && state->sessionkey && state->clientqop & GSASL_QOP_AUTH_CONF)
545     {
546       return GSASL_INTEGRITY_ERROR;
547     }
548   else if (state && state->sessionkey
549 	   && state->clientqop & GSASL_QOP_AUTH_INT)
550     {
551       Shishi_asn1 asn1safe;
552 
553       res = shishi_safe (state->sh, &state->safe);
554       if (res != SHISHI_OK)
555 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
556 
557       res = shishi_safe_safe_der_set (state->safe, input, input_len);
558       if (res != SHISHI_OK)
559 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
560 
561       res = shishi_safe_verify (state->safe, state->sessionkey);
562       if (res != SHISHI_OK)
563 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
564 
565       res = shishi_safe_user_data (state->sh, shishi_safe_safe (state->safe),
566 				   output, output_len);
567       if (res != SHISHI_OK)
568 	return GSASL_KERBEROS_V5_INTERNAL_ERROR;
569 
570       return GSASL_OK;
571     }
572   else
573     {
574       *output_len = input_len;
575       *output = malloc (input_len);
576       if (!*output)
577 	return GSASL_MALLOC_ERROR;
578       memcpy (*output, input, input_len);
579     }
580 
581 
582   return GSASL_OK;
583 }
584 
585 int
_gsasl_kerberos_v5_server_finish(Gsasl_session * sctx,void * mech_data)586 _gsasl_kerberos_v5_server_finish (Gsasl_session * sctx, void *mech_data)
587 {
588   struct _Gsasl_kerberos_v5_server_state *state = mech_data;
589 
590   shishi_done (state->sh);
591 
592   free (state->username);
593   free (state->password);
594   free (state->random);
595   free (state);
596 
597   return GSASL_OK;
598 }
599