/*
 * $Id: author.c,v 1.7 2006/12/13 01:11:36 heas Exp $
 *
 * Copyright (c) 1995-1998 by Cisco systems, Inc.
 *
 * Permission to use, copy, modify, and distribute this software for
 * any purpose and without fee is hereby granted, provided that this
 * copyright and permission notice appear on all copies of the
 * software and supporting documentation, the name of Cisco Systems,
 * Inc. not be used in advertising or publicity pertaining to
 * distribution of the program without specific prior permission, and
 * notice be given in supporting documentation that modification,
 * copying and distribution is by permission of Cisco Systems, Inc.
 *
 * Cisco Systems, Inc. makes no representations about the suitability
 * of this software for any purpose.  THIS SOFTWARE IS PROVIDED ``AS
 * IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "tac_plus.h"

/*
 *  Come here when we receive an authorization START packet
 */
void
author(u_char *pak)
{
    HDR *hdr;
    struct author *apak;
    struct identity identity;
#ifdef ACLS
    struct authen_data authen_data;
#endif
    struct author_data author_data;
    u_char *p;
    u_char *argsizep;
    char **cmd_argp;
    int i, len;

    if (debug & DEBUG_AUTHOR_FLAG)
	report(LOG_DEBUG, "Start authorization request");

    hdr = (HDR *)pak;
    apak = (struct author *) (pak + TAC_PLUS_HDR_SIZE);

    /* Do some sanity checks */
    if (hdr->seq_no != 1) {
	send_error_reply(TAC_PLUS_AUTHOR, NULL);
	return;
    }

    /* arg counts start here */
    p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;

    /* Length checks */
    len = TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;
    len += apak->user_len + apak->port_len + apak->rem_addr_len + apak->arg_cnt;
    for (i = 0; i < (int)apak->arg_cnt; i++) {
	len += p[i];
    }

    if (len != ntohl(hdr->datalength)) {
	send_error_reply(TAC_PLUS_AUTHOR, NULL);
	return;
    }

    /* start of variable length data is here */
    p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;

    /* arg length data starts here */
    argsizep = p;

    p += apak->arg_cnt;

    bzero(&author_data, sizeof(struct author_data));

    /* The identity structure */

    /* zero out identity struct */
    bzero(&identity, sizeof(struct identity));
    identity.username = tac_make_string(p, (int) apak->user_len);
    p += apak->user_len;

    identity.NAS_name = tac_strdup(session.peer);
#ifdef ACLS
    identity.NAS_ip = tac_strdup(session.peerip);
#endif

    identity.NAS_port = tac_make_string(p, (int)apak->port_len);
    p += apak->port_len;
    if (apak->port_len <= 0) {
	strcpy(session.port, "unknown-port");
    } else {
	strcpy(session.port, identity.NAS_port);
    }

    identity.NAC_address = tac_make_string(p, (int)apak->rem_addr_len);
    p += apak->rem_addr_len;

    identity.priv_lvl = apak->priv_lvl;

    /* The author_data structure */

    author_data.id = &identity;	/* user id */

    /* FIXME: validate these fields */
    author_data.authen_method = apak->authen_method;
    author_data.authen_type = apak->authen_type;
    author_data.service = apak->service;
    author_data.num_in_args = apak->arg_cnt;

    /* Space for args + NULL */
    cmd_argp = (char **) tac_malloc(apak->arg_cnt * sizeof(char *));

    /* p  points to the start of args. Step thru them making strings */
    for (i = 0; i < (int)apak->arg_cnt; i++) {
	cmd_argp[i] = tac_make_string(p, *argsizep);
	p += *argsizep++;
    }

    author_data.input_args = cmd_argp;	/* input command arguments */

#ifdef ACLS
    authen_data.NAS_id = &identity;
    if (verify_host(author_data.id->username,
			&authen_data, S_acl, TAC_PLUS_RECURSE) != S_permit) {
	author_data.status = AUTHOR_STATUS_FAIL;
    } else
#endif
    if (do_author(&author_data)) {
	report(LOG_ERR, "%s: do_author returned an error", session.peer);
	send_author_reply(AUTHOR_STATUS_ERROR,
			  author_data.msg,
			  author_data.admin_msg,
			  author_data.num_out_args,
			  author_data.output_args);
	return;
    }

    /* Send a reply packet */
    send_author_reply(author_data.status,
		      author_data.msg,
		      author_data.admin_msg,
		      author_data.num_out_args,
		      author_data.output_args);

    if (debug)
	report(LOG_INFO, "authorization query for '%s' %s from %s %s",
	       author_data.id->username  && author_data.id->username[0] ?
	       author_data.id->username : "unknown",
	       author_data.id->NAS_port && author_data.id->NAS_port[0] ?
	       author_data.id->NAS_port : "unknown",
	       session.peer,
	       (author_data.status == AUTHOR_STATUS_PASS_ADD ||
		author_data.status == AUTHOR_STATUS_PASS_REPL) ?
	       "accepted" : "rejected");

    /* free the input args */
    if (author_data.input_args) {
	for (i = 0; i < author_data.num_in_args; i++)
	    free(author_data.input_args[i]);

	free(author_data.input_args);
	author_data.input_args = NULL;
    }

    /* free the output args */
    if (author_data.output_args) {
	for (i = 0; i < author_data.num_out_args; i++)
	    free(author_data.output_args[i]);

	free(author_data.output_args);
	author_data.output_args = NULL;
    }

    if (author_data.msg)
	free(author_data.msg);

    if (author_data.admin_msg)
	free(author_data.admin_msg);

    free(identity.username);
    free(identity.NAS_name);
    free(identity.NAS_port);
    free(identity.NAC_address);
}


syntax highlighted by Code2HTML, v. 0.9.1