/*
 * Copyright (C) 1998 by the FundsXpress, INC.
 * 
 * All rights reserved.
 * 
 * Export of this software from the United States of America may require
 * a specific license from the United States Government.  It is the
 * responsibility of any person or organization contemplating export to
 * obtain such a license before exporting.
 * 
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of FundsXpress. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  FundsXpress makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "gssapiP_krb5.h"

/* from the token, flags is stored directly. nctypes/ctypes is
   allocated and returns the length and list of ctypes in the token.
   noptions/options lists all the options which the caller cares
   about.  Those which are present in the token are filled in; the
   order and length are not changed.  If an error is returned, the
   option list is in an indeterminate state. */

OM_uint32
kg2_parse_token(minor_status, ptr, token_length, flags, nctypes, ctypes,
		noptions, options, kmsg, mic)
     OM_uint32 *minor_status;
     unsigned char *ptr;
     int token_length;
     krb5_ui_4 *flags;
     int *nctypes; /* OUT */
     krb5_cksumtype **ctypes; /* OUT */
     int noptions; 
     struct kg2_option *options; /* INOUT */
     krb5_data *kmsg;
     krb5_data *mic;
{
    int field_length, i;
    int opt_id;

    *ctypes = 0;

    /* read the flags */

    if (token_length < 4)
	goto defective;
    *flags = (ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3];
    ptr += 4;
    token_length -= 4;

    /* read out the token list */

    if (token_length < 2)
	goto defective;
    field_length = (ptr[0]<<8) | ptr[1];
    ptr += 2;
    token_length -= 2;

    *nctypes = field_length;

    if (*nctypes == 0) {
	*minor_status = 0;
	return(GSS_S_DEFECTIVE_TOKEN);
    }

    if ((*ctypes = (krb5_cksumtype *)
	 malloc((*nctypes) * sizeof(krb5_cksumtype))) == NULL) {
	*minor_status = ENOMEM;
	return(GSS_S_FAILURE);
    }

    for (i=0; i<field_length; i++) {
	if (token_length < 4)
	    goto defective;

	(*ctypes)[i] = (krb5_cksumtype) ((ptr[0]<<24) | (ptr[1]<<16) |
				      (ptr[2]<<8) | ptr[3]);
	ptr += 4;
	token_length -= 4;
    }

    do {
	if (token_length < 4)
	    goto defective;
	opt_id = (ptr[0]<<8) | ptr[1];
	field_length = (ptr[2]<<8) | ptr[3];
	ptr += 4;
	token_length -= 4;

	if (token_length < field_length)
	    goto defective;

	for (i=0; i<noptions; i++) {
	    if (options[i].option_id = opt_id) {
		options[i].length = field_length;
		options[i].data = ptr;
	    }
	    break;
	}
	    
	ptr += field_length;
	token_length -= field_length;
    } while (opt_id);

    if (token_length < 2)
	goto defective;
    field_length = (ptr[0]<<8) | ptr[1];
    ptr += 2;
    token_length -= 2;

    if (token_length < field_length)
	goto defective;

    kmsg->length = field_length;
    kmsg->data = ptr;

    ptr += field_length;
    token_length -= field_length;

    /* if there's anything left, assume it's a mic.  the mic isn't
       necessarily present */

    if (mic && token_length) {
	if (token_length < 2)
	    goto defective;
	field_length = (ptr[0]<<8) | ptr[1];
	ptr += 2;
	token_length -= 2;

	if (token_length < field_length)
	    goto defective;

	mic->length = field_length;
	mic->data = ptr;

	ptr += field_length;
	token_length -= field_length;
    } else if (mic) {
	mic->length = 0;
	mic->data = ptr;
    }

    if (token_length)
	goto defective;

    return(GSS_S_COMPLETE);

defective:
    if (*ctypes)
	free(*ctypes);

    *minor_status = 0;
    return(GSS_S_DEFECTIVE_TOKEN);
}
    
/* nc1/c1 will be modified to contain the intersection of the
   two lists. */

void
kg2_intersect_ctypes(nc1, c1, nc2, c2)
     int *nc1;
     krb5_cksumtype *c1;
     int nc2;
     const krb5_cksumtype *c2;
{
    int i, j, count;
    krb5_cksumtype tmp;

    count = 0;

    for (i=0; i<*nc1; i++) {
	/* first, check to make sure that c1[i] isn't a duplicate in c1 */
	for (j=0; j<i; j++)
	    if (c1[i] == c1[j])
		break;
	if (j<i)
	    continue;
	/* check if c1[i] is in c2.  If it is, keep it by swapping
	   it into c1[count] and incrementing count.  If count < i, then
	   that field has already been looked at and skipped as
	   not intersecting, which is ok. */

	for (j=0; j<nc2; j++)
	    if (c1[i] == c2[j])
		break;
	if ((j<nc2) && (count != i)) {
	    tmp = c1[count];
	    c1[count] = c1[i];
	    c1[i] = tmp;
	}
	count++;
    }

    *nc1 = count;
}

