Exemple #1
0
/* §5.2 */
static int answer_REQ_VOTE(
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
		)
{
	int valid;
	struct boothc_ticket_msg omsg;
	cmd_result_t inappr_reason;
	int reason;

	inappr_reason = test_reason(tk, sender, leader, msg);
	if (inappr_reason)
		return send_reject(sender, tk, inappr_reason, msg);

	valid = term_time_left(tk);
	reason = ntohl(msg->header.reason);

	/* valid tickets are not allowed only if the sender thinks
	 * the ticket got lost */
	if (sender != tk->leader && valid && reason != OR_STEPDOWN) {
		tk_log_warn("election from %s with reason %s rejected "
			"(we have %s as ticket owner), ticket still valid for %ds",
			site_string(sender), state_to_string(reason),
			site_string(tk->leader), valid);
		return send_reject(sender, tk, RLT_TERM_STILL_VALID, msg);
	}

	if (term_too_low(tk, sender, leader, msg))
		return 0;

	/* set this, so that we know not to send status for the
	 * ticket */
	tk->in_election = 1;

	/* reset ticket's leader on not valid tickets */
	if (!valid)
		set_leader(tk, NULL);

	/* if it's a newer term or ... */
	if (newer_term(tk, sender, leader, msg, 1)) {
		clear_election(tk);
		goto vote_for_sender;
	}


	/* ... we didn't vote yet, then vote for the sender */
	/* §5.2, §5.4 */
	if (!tk->voted_for) {
vote_for_sender:
		tk->voted_for = sender;
		record_vote(tk, sender, leader);
	}


	init_ticket_msg(&omsg, OP_VOTE_FOR, OP_REQ_VOTE, RLT_SUCCESS, 0, tk);
	omsg.ticket.leader = htonl(get_node_id(tk->voted_for));
	return booth_udp_send_auth(sender, &omsg, sendmsglen(&omsg));
}
Exemple #2
0
static int process_UPDATE (
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
	       )
{
	if (is_owned(tk) && sender != tk->leader) {
		tk_log_warn("different leader %s wants to update "
				"our ticket, sending reject",
			site_string(leader));
		return send_reject(sender, tk, RLT_TERM_OUTDATED, msg);
	}

	tk_log_debug("leader %s wants to update our ticket",
			site_string(leader));

	become_follower(tk, msg);
	set_leader(tk, leader);
	ticket_write(tk);

	/* run ticket_cron if the ticket expires */
	set_ticket_wakeup(tk);

	return send_msg(OP_ACK, tk, sender, msg);
}
Exemple #3
0
static int term_too_low(struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg)
{
	uint32_t term;

	term = ntohl(msg->ticket.term);
	/* §5.1 */
	if (term < tk->current_term) {
		tk_log_info("sending reject to %s, its term too low "
			"(%d vs. %d)", site_string(sender),
			term, tk->current_term
			);
		send_reject(sender, tk, RLT_TERM_OUTDATED, msg);
		return 1;
	}

	return 0;
}
Exemple #4
0
/* For follower. */
static int answer_HEARTBEAT (
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
	       )
{
	uint32_t term;

	term = ntohl(msg->ticket.term);
	tk_log_debug("heartbeat from leader: %s, have %s; term %d vs %d",
			site_string(leader), ticket_leader_string(tk),
			term, tk->current_term);

	if (term < tk->current_term) {
		if (sender == tk->leader) {
			tk_log_info("trusting leader %s with a lower term (%d vs %d)",
				site_string(leader), term, tk->current_term);
		} else if (is_owned(tk)) {
			tk_log_warn("different leader %s with a lower term "
					"(%d vs %d), sending reject",
				site_string(leader), term, tk->current_term);
			return send_reject(sender, tk, RLT_TERM_OUTDATED, msg);
		}
	}

	/* got heartbeat, no rejects expected anymore */
	tk->expect_more_rejects = 0;

	/* Needed? */
	newer_term(tk, sender, leader, msg, 0);

	become_follower(tk, msg);
	/* Racy??? */
	assert(sender == leader || !leader);

	set_leader(tk, leader);

	/* Ack the heartbeat (we comply). */
	return send_msg(OP_ACK, tk, sender, msg);
}
Exemple #5
0
/**
 * Every time a new dialog is created (from a new INVITE) the dialog
 * module will call this callback function. We need to track the
 * dialogs lifespan from this point forward until it is terminated
 * with a BYE, CANCEL, etc. In the process, we will see if either or
 * both ends of the conversation supports SIP Session Timers and setup
 * the dialog timeout to expire at the session timer expire time. Each
 * time the new re-INVITE is seen to update the SST, we will reset the
 * life span of the dialog to match it.
 *
 * This function will setup the other types of dialog callbacks
 * required to track the lifespan of the dialog. It will also start
 * the state tracking to figure out if and who supports SST.
 *
 * As per RFC4028: Request handling:
 *
 * - The proxy may insert a SE header if none found.
 * - The SE value can be anything >= Min-SE (if found)
 * - The proxy MUST NOT add a refresher parameter to the SE.
 *
 * - If SE is already there, the Proxy can reduce its value but no
 *   lower then the Min-SE value if present.
 * - If the SE value is >= Min-SE the proxy MUST NOT increase it!
 * - If the SE value is < Min-SE (settable by the proxy) the proxy
 *   MUST increase the SE value to >= the new Min-SE.
 * - The proxy MUST NOT insert or change the refresher parameter.
 *
 * - If the supported=timer is found, the proxy may reject the request
 *   with a 422 if the SE value is smaller then the local policy. The
 *   422 MUST hold the proxies Min-SE value >= 90.
 * - If support=timer is NOT indecated, the proxy can't reject with a
 *   422 but can include/increase the MIN-SE: to be = to local policy.
 *   and increase the SE to match the new Min-SE value.
 * - the proxy MUST NOT insert/change the Min-SE header if
 *   supported=timer is present. (DoS attacks)
 *
 * @param did - The dialog ID
 * @param type - The trigger event type (CREATED)
 * @param params - The pointer to nothing. As we did not attach
 *                anything to this callback in the dialog module.
 */
void sst_dialog_created_CB(struct dlg_cell *did, int type,
		struct dlg_cb_params * params)
{
	sst_info_t *info = NULL;
	sst_msg_info_t minfo;
	struct sip_msg* msg = params->msg;

	memset(&minfo, 0, sizeof(sst_msg_info_t));
	/*
	 * Only deal with messages flaged as SST interested.
	 */
	if ((msg->flags & sst_flag) != sst_flag) {
		LM_DBG("SST flag was not set for this request\n");
		return;
	}

	/*
	 * look only at INVITE
	 */
	if (msg->first_line.type != SIP_REQUEST ||
			msg->first_line.u.request.method_value != METHOD_INVITE) {
		LM_WARN("dialog create callback called with a non-INVITE request.\n");
		return;
	}

	/*
	 * Gather all he information about SST for this message
	 */
	if (parse_msg_for_sst_info(msg, &minfo)) {
		LM_ERR("failed to parse sst information\n");
		return;
	}

	info = (sst_info_t *)shm_malloc(sizeof(sst_info_t));
	memset(info, 0, sizeof(sst_info_t));
	info->requester = (minfo.se?SST_UAC:SST_UNDF);
	info->supported = (minfo.supported?SST_UAC:SST_UNDF);
	info->interval = MAX(sst_interval, 90); /* For now, will set for real
										  * later */

	if (minfo.se != 0) {
		/*
		 * There is a SE already there, this is good, we just need to
		 * check the values out a little before passing it along.
		 */
		if (minfo.se < sst_min_se) {
			/*
			 * Problem, the requested Session-Expires is too small for
			 * our local policy. We need to fix it, or reject it or
			 * ignore it.
			 */
			if (!minfo.supported) {
				/*
				 * Increase the Min-SE: value in the request and
				 * forward it.
				 */
				char buf[80];
				if (minfo.min_se) {
					/* We need to update, which means, remove +
					 * insert */
					remove_minse_header(msg);
				}
				info->interval = MAX(sst_min_se, minfo.min_se);
				snprintf(buf, 80, "Min-SE: %d\r\n", info->interval);
				if (append_header(msg, buf)) {
					LM_ERR("Could not append modified Min-SE: header\n");
				}
			}
			else if (sst_reject) {
				/* Make sure that that all are at least 90 */
				send_reject(msg, MAX(MAX(sst_min_se, minfo.min_se), 90));
				shm_free(info);
				return;
			}
		}  /* end of se < sst_min_se */
		else {
			/* Use the INVITE SE: value */
			info->interval = minfo.se;
		}
	}
	else {
		/*
		 * No Session-Expire: stated in request.
		 */
		char buf[80];

		info->interval = MAX(minfo.min_se, sst_min_se);

		if (minfo.min_se && minfo.min_se < sst_min_se) {
			remove_minse_header(msg);
			snprintf(buf, 80, "Min-SE: %d\r\n", info->interval);
			if (append_header(msg, buf)) {
				LM_ERR("failed to append modified Min-SE: header\n");
				/* What to do? Let is slide, we can still work */
			}
		}

		info->interval = MAX(info->interval, sst_interval);
		info->requester = SST_PXY;
		snprintf(buf, 80, "Session-Expires: %d\r\n", info->interval);
		if (append_header(msg, buf)) {
			LM_ERR("failed to append Session-Expires header to proxy "
					"requested SST.\n");
			shm_free(info);
			return; /* Nothing we can do! */
		}
	}
	/* We keep the sst_info in the dialog's vals in case of restarting */
	/* No const here because of store_dlg_value's definition */
	str raw_info = {(char*)info, sizeof(sst_info_t)};
	if (dlg_binds->store_dlg_value(did, &info_val_name, &raw_info) != 0) {
		LM_ERR("No sst_info can be added to the dialog."
				"This dialog won't be considered after restart!\n");
	}

	dlg_binds->set_mod_flag(did, SST_DIALOG_FLAG);

	setup_dialog_callbacks(did, info);
	/* Early setup of default timeout */
	set_dialog_lifetime(did, info->interval);
	return;
}
Exemple #6
0
int raft_answer(
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
	       )
{
	int cmd, req;
	int rv;

	rv = 0;
	cmd = ntohl(msg->header.cmd);
	req = ntohl(msg->header.request);

	if (req)
		tk_log_debug("got %s (req %s) from %s",
				state_to_string(cmd),
				state_to_string(req),
				site_string(sender));
	else
		tk_log_debug("got %s from %s",
				state_to_string(cmd),
				site_string(sender));

	/* don't process tickets with invalid term
	 */
	if (cmd != OP_STATUS &&
			msg_term_invalid(tk, sender, leader, msg))
		return 0;


	switch (cmd) {
	case OP_REQ_VOTE:
		rv = answer_REQ_VOTE(tk, sender, leader, msg);
		break;
	case OP_VOTE_FOR:
		rv = process_VOTE_FOR(tk, sender, leader, msg);
		break;
	case OP_ACK:
		if (tk->leader == local &&
				tk->state == ST_LEADER)
			rv = process_ACK(tk, sender, leader, msg);
		break;
	case OP_HEARTBEAT:
		if ((tk->leader != local || !term_time_left(tk)) &&
				(tk->state == ST_INIT || tk->state == ST_FOLLOWER ||
				tk->state == ST_CANDIDATE))
			rv = answer_HEARTBEAT(tk, sender, leader, msg);
		else {
			tk_log_warn("unexpected message %s, from %s",
				state_to_string(cmd),
				site_string(sender));
			if (ticket_seems_ok(tk))
				send_reject(sender, tk, RLT_TERM_STILL_VALID, msg);
			rv = -EINVAL;
		}
		break;
	case OP_UPDATE:
		if (((tk->leader != local && tk->leader == leader) || !is_owned(tk)) &&
				(tk->state == ST_INIT || tk->state == ST_FOLLOWER ||
				tk->state == ST_CANDIDATE)) {
			rv = process_UPDATE(tk, sender, leader, msg);
		} else {
			tk_log_warn("unexpected message %s, from %s",
				state_to_string(cmd),
				site_string(sender));
			if (ticket_seems_ok(tk))
				send_reject(sender, tk, RLT_TERM_STILL_VALID, msg);
			rv = -EINVAL;
		}
		break;
	case OP_REJECTED:
		rv = process_REJECTED(tk, sender, leader, msg);
		break;
	case OP_REVOKE:
		rv = process_REVOKE(tk, sender, leader, msg);
		break;
	case OP_MY_INDEX:
		rv = process_MY_INDEX(tk, sender, leader, msg);
		break;
	case OP_STATUS:
		if (!tk->in_election)
			rv = send_msg(OP_MY_INDEX, tk, sender, msg);
		break;
	default:
		tk_log_error("unknown message %s, from %s",
			state_to_string(cmd), site_string(sender));
		rv = -EINVAL;
	}
	return rv;
}
OM_uint32 KRB5_LIB_FUNCTION gss_accept_sec_context_spnego
           (OM_uint32 * minor_status,
            gss_ctx_id_t * context_handle,
            const gss_cred_id_t acceptor_cred_handle,
            const gss_buffer_t input_token_buffer,
            const gss_channel_bindings_t input_chan_bindings,
            gss_name_t * src_name,
            gss_OID * mech_type,
            gss_buffer_t output_token,
            OM_uint32 * ret_flags,
            OM_uint32 * time_rec,
            gss_cred_id_t * delegated_cred_handle)
{
   NegTokenInit init_token;
   OM_uint32 major_status;
   OM_uint32 minor_status2;
   gss_buffer_desc ibuf, obuf;
   gss_buffer_t ot = NULL;
   unsigned char *buf;
   size_t buf_size;
   size_t len, taglen, ni_len;
   int found = 0;
   int ret, i;

   memset(&init_token, 0, sizeof(init_token));

   ret = gssapi_spnego_decapsulate(minor_status, input_token_buffer,
	 			   &buf, &buf_size, GSS_SPNEGO_MECH);
   if (ret)
      return ret;

   ret = der_match_tag_and_length(buf, buf_size, KERB_CTXT, CONS,
	 			  0, &len, &taglen);
   if (ret)
      return ret;

   ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len);
   if (ret) {
      *minor_status = EINVAL; /* XXX */
      return GSS_S_DEFECTIVE_TOKEN;
   }

   if (init_token.mechTypes == NULL)
      return send_reject (minor_status, output_token);

   for (i = 0; !found && i < init_token.mechTypes->len; ++i) {
      unsigned char mechbuf[17];
      size_t mech_len;

      ret = der_put_oid (mechbuf + sizeof(mechbuf) - 1,
                         sizeof(mechbuf),
                         &init_token.mechTypes->val[i],
                         &mech_len);
      if (ret)
          return GSS_S_DEFECTIVE_TOKEN;
      if (mech_len == GSS_KRB5_MECH->length
          && memcmp(GSS_KRB5_MECH->elements,
                    mechbuf + sizeof(mechbuf) - mech_len,
                    mech_len) == 0)
          found = 1;
   }

   if (!found)
      return send_reject (minor_status, output_token);

   if (init_token.mechToken != NULL) {
      ibuf.length = init_token.mechToken->length;
      ibuf.value  = init_token.mechToken->data;

      major_status = gss_accept_sec_context(minor_status,
	    				    context_handle,
					    acceptor_cred_handle,
					    &ibuf,
					    input_chan_bindings,
					    src_name,
					    mech_type,
					    &obuf,
					    ret_flags,
					    time_rec,
					    delegated_cred_handle);
      if (GSS_ERROR(major_status)) {
	 send_reject (&minor_status2, output_token);
	 return major_status;
      }
      ot = &obuf;
   }

   ret = send_accept (&minor_status2, output_token, ot);
   if (ot != NULL)
      gss_release_buffer(&minor_status2, ot);

   return ret;
}
Exemple #8
0
static OM_uint32 GSSAPI_CALLCONV
acceptor_continue
	   (OM_uint32 * minor_status,
	    gss_ctx_id_t * context_handle,
	    const gss_cred_id_t acceptor_cred_handle,
	    const gss_buffer_t input_token_buffer,
	    const gss_channel_bindings_t input_chan_bindings,
	    gss_name_t * src_name,
	    gss_OID * mech_type,
	    gss_buffer_t output_token,
	    OM_uint32 * ret_flags,
	    OM_uint32 * time_rec,
	    gss_cred_id_t *delegated_cred_handle
	   )
{
    OM_uint32 ret, ret2, minor;
    NegotiationToken nt;
    size_t nt_len;
    NegTokenResp *na;
    unsigned int negResult = accept_incomplete;
    gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
    gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
    gss_buffer_desc mech_buf;
    gssspnego_ctx ctx;

    mech_buf.value = NULL;

    ctx = (gssspnego_ctx)*context_handle;

    /*
     * The GSS-API encapsulation is only present on the initial
     * context token (negTokenInit).
     */

    ret = decode_NegotiationToken(input_token_buffer->value,
				  input_token_buffer->length,
				  &nt, &nt_len);
    if (ret) {
	*minor_status = ret;
	return GSS_S_DEFECTIVE_TOKEN;
    }
    if (nt.element != choice_NegotiationToken_negTokenResp) {
	*minor_status = 0;
	return GSS_S_DEFECTIVE_TOKEN;
    }
    na = &nt.u.negTokenResp;

    if (na->negResult != NULL) {
	negResult = *(na->negResult);
    }

    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);

    {
	gss_buffer_desc ibuf, obuf;
	int require_mic, get_mic = 0;
	int require_response;
	heim_octet_string *mic;

	if (na->responseToken != NULL) {
	    ibuf.length = na->responseToken->length;
	    ibuf.value = na->responseToken->data;
	    mech_input_token = &ibuf;
	} else {
	    ibuf.value = NULL;
	    ibuf.length = 0;
	}

	if (mech_input_token != GSS_C_NO_BUFFER) {

	    if (ctx->mech_src_name != GSS_C_NO_NAME)
		gss_release_name(&minor, &ctx->mech_src_name);

	    ret = gss_accept_sec_context(&minor,
					 &ctx->negotiated_ctx_id,
					 acceptor_cred_handle,
					 mech_input_token,
					 input_chan_bindings,
					 &ctx->mech_src_name,
					 &ctx->negotiated_mech_type,
					 &obuf,
					 &ctx->mech_flags,
					 &ctx->mech_time_rec,
					 delegated_cred_handle);

	    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
		mech_output_token = &obuf;
	    }
	    if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
		free_NegotiationToken(&nt);
		gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
		send_reject (minor_status, output_token);
		HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
		return ret;
	    }
	    if (ret == GSS_S_COMPLETE)
		ctx->open = 1;
	} else
	    ret = GSS_S_COMPLETE;

	ret2 = _gss_spnego_require_mechlist_mic(minor_status,
						ctx,
						&require_mic);
	if (ret2)
	    goto out;

	ctx->require_mic = require_mic;

	mic = na->mechListMIC;
	if (mic != NULL)
	    require_mic = 1;

	if (ret == GSS_S_COMPLETE)
	    ret = acceptor_complete(minor_status,
				    ctx,
				    &get_mic,
				    &mech_buf,
				    mech_input_token,
				    mech_output_token,
				    na->mechListMIC,
				    output_token);

	if (ctx->mech_flags & GSS_C_DCE_STYLE)
	    require_response = (negResult != accept_completed);
	else
	    require_response = 0;

	/*
	 * Check whether we need to send a result: there should be only
	 * one accept_completed response sent in the entire negotiation
	 */
	if ((mech_output_token != GSS_C_NO_BUFFER &&
	     mech_output_token->length != 0)
	    || (ctx->open && negResult == accept_incomplete)
	    || require_response
	    || get_mic) {
	    ret2 = send_accept (minor_status,
				ctx,
				mech_output_token,
				0,
				get_mic ? &mech_buf : NULL,
				output_token);
	    if (ret2)
		goto out;
	}

     out:
	if (ret2 != GSS_S_COMPLETE)
	    ret = ret2;
	if (mech_output_token != NULL)
	    gss_release_buffer(&minor, mech_output_token);
	if (mech_buf.value != NULL)
	    free(mech_buf.value);
	free_NegotiationToken(&nt);
    }

    if (ret == GSS_S_COMPLETE) {
	if (src_name != NULL && ctx->mech_src_name != NULL) {
	    spnego_name name;

	    name = calloc(1, sizeof(*name));
	    if (name) {
		name->mech = ctx->mech_src_name;
		ctx->mech_src_name = NULL;
		*src_name = (gss_name_t)name;
	    }
	}
    }

    if (mech_type != NULL)
	*mech_type = ctx->negotiated_mech_type;
    if (ret_flags != NULL)
	*ret_flags = ctx->mech_flags;
    if (time_rec != NULL)
	*time_rec = ctx->mech_time_rec;

    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
 	return ret;
    }

    _gss_spnego_internal_delete_sec_context(&minor, context_handle,
				   GSS_C_NO_BUFFER);

    return ret;
}
Exemple #9
0
static OM_uint32
acceptor_complete(OM_uint32 * minor_status,
		  gssspnego_ctx ctx,
		  int *get_mic,
		  gss_buffer_t mech_buf,
		  gss_buffer_t mech_input_token,
		  gss_buffer_t mech_output_token,
		  heim_octet_string *mic,
		  gss_buffer_t output_token)
{
    OM_uint32 ret;
    int require_mic, verify_mic;
    gss_buffer_desc buf;

    buf.length = 0;
    buf.value = NULL;

    ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
    if (ret)
	return ret;

    ctx->require_mic = require_mic;

    if (mic != NULL)
	require_mic = 1;

    if (ctx->open && require_mic) {
	if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
	    verify_mic = 1;
	    *get_mic = 0;
	} else if (mech_output_token != GSS_C_NO_BUFFER &&
		   mech_output_token->length == 0) { /* Odd */
	    *get_mic = verify_mic = 1;
	} else { /* Even/One */
	    verify_mic = 0;
	    *get_mic = 1;
	}
	
	if (verify_mic || *get_mic) {
	    int eret;
	    size_t buf_len;
	
	    ASN1_MALLOC_ENCODE(MechTypeList,
			       mech_buf->value, mech_buf->length,
			       &ctx->initiator_mech_types, &buf_len, eret);
	    if (eret) {
		*minor_status = eret;
		return GSS_S_FAILURE;
	    }
	    if (buf.length != buf_len)
		abort();
	}
	
	if (verify_mic) {
	    ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
	    if (ret) {
		if (*get_mic)
		    send_reject (minor_status, output_token);
		if (buf.value)
		    free(buf.value);
		return ret;
	    }
	    ctx->verified_mic = 1;
	}
	if (buf.value)
	    free(buf.value);

    } else
	*get_mic = 0;

    return GSS_S_COMPLETE;
}
Exemple #10
0
/**
 * Every time a new dialog is created (from a new INVITE) the dialog
 * module will call this callback function. We need to track the
 * dialogs lifespan from this point forward until it is terminated
 * with a BYE, CANCEL, etc. In the process, we will see if either or
 * both ends of the conversation supports SIP Session Timers and setup
 * the dialog timeout to expire at the session timer expire time. Each
 * time the new re-INVITE is seen to update the SST, we will reset the
 * life span of the dialog to match it.
 *
 * This function will setup the other types of dialog callbacks
 * required to track the lifespan of the dialog. It will also start
 * the state tracking to figure out if and who supports SST.
 *
 * As per RFC4028: Request handling:
 * 
 * - The proxy may insert a SE header if none found.
 * - The SE value can be anything >= Min-SE (if found)
 * - The proxy MUST NOT add a refresher parameter to the SE.
 *
 * - If SE is already there, the Proxy can reduce its value but no
 *   lower then the Min-SE value if present.
 * - If the SE value is >= Min-SE the proxy MUST NOT increase it!
 * - If the SE value is < Min-SE (settable by the proxy) the proxy
 *   MUST increase the SE value to >= the new Min-SE.
 * - The proxy MUST NOT insert or change the refresher parameter.
 *
 * - If the supported=timer is found, the proxy may reject the request
 *   with a 422 if the SE value is smaller then the local policy. The
 *   422 MUST hold the proxies Min-SE value >= 90.
 * - If support=timer is NOT indecated, the proxy can't reject with a
 *   422 but can include/increase the MIN-SE: to be = to local policy.
 *   and increase the SE to match the new Min-SE value.
 * - the proxy MUST NOT insert/change the Min-SE header if
 *   supported=timer is present. (DoS attacks)
 *
 * @param did - The dialog ID
 * @param type - The trigger event type (CREATED)
 * @param params - The pointer to nothing. As we did not attach
 *                anything to this callback in the dialog module.
 */
void sst_dialog_created_CB(struct dlg_cell *did, int type,
		struct dlg_cb_params * params)
{
	sst_info_t *info = NULL;
	sst_msg_info_t minfo;
	struct sip_msg* msg = params->req;

	memset(&minfo, 0, sizeof(sst_msg_info_t));
	/*
	 * Only deal with messages flaged as SST interested.
	 */
	if ((msg->flags & sst_flag) != sst_flag) {
		LM_DBG("SST flag was not set for this request\n");
		return;
	}

	/* 
	 * look only at INVITE
	 */
	if (msg->first_line.type != SIP_REQUEST ||
			msg->first_line.u.request.method_value != METHOD_INVITE) {
		LM_WARN("dialog create callback called with a non-INVITE request.\n");
		return;
	}

	/*
	 * Gather all he information about SST for this message
	 */
	if (parse_msg_for_sst_info(msg, &minfo)) {
		LM_ERR("failed to parse sst information\n");
		return;
	}

	info = (sst_info_t *)shm_malloc(sizeof(sst_info_t));
	memset(info, 0, sizeof(sst_info_t));
	info->requester = (minfo.se?SST_UAC:SST_UNDF);
	info->supported = (minfo.supported?SST_UAC:SST_UNDF);
	info->interval = MAX(sst_min_se, 90); /* For now, will set for real
										  * later */

	if (minfo.se != 0) {
		/* 
		 * There is a SE already there, this is good, we just need to
		 * check the values out a little before passing it along.
		 */
		if (minfo.se < sst_min_se) {
			/* 
			 * Problem, the requested Session-Expires is too small for
			 * our local policy. We need to fix it, or reject it or
			 * ignore it.
			 */
			if (!minfo.supported) {
				/* 
				 * Increase the Min-SE: value in the request and
				 * forward it.
				 */
				str msehdr;
				if (minfo.min_se) {
					/* We need to update, which means, remove +
					 * insert */
					remove_header(msg, "Min-SE");
				}
				info->interval = MAX(sst_min_se, minfo.min_se);
				sst_build_minse_hdr(info->interval, &msehdr);
				if (append_header(msg, msehdr.s)) {
					LM_ERR("Could not append modified Min-SE: header\n");
				}
			}
			else if (sst_reject) {
				/* Make sure that that all are at least 90 */
				send_reject(msg, MAX(MAX(sst_min_se, minfo.min_se), 90));
				shm_free(info);
				return;
			}
		}  /* end of se < sst_min_se */
		else {
			/* Use the INVITE SE: value */
			info->interval = minfo.se;
		}
	}
	else {
		/* 
		 * No Session-Expire: stated in request.
		 */
		str msehdr;

		info->interval = MAX(minfo.min_se, sst_min_se);

		if (minfo.min_se && minfo.min_se < sst_min_se) {
			remove_header(msg, "Min-SE");
			sst_build_minse_hdr(info->interval, &msehdr);
			if (append_header(msg, msehdr.s)) {
				LM_ERR("failed to append modified Min-SE: header\n");
				/* What to do? Let is slide, we can still work */
			}
		}
		
		info->requester = SST_PXY;
		sst_build_se_hdr(info->interval, &msehdr, NULL);
		if (append_header(msg, msehdr.s)) {
			LM_ERR("failed to append Session-Expires header to proxy "
					"requested SST.\n");
			shm_free(info);
			return; /* Nothing we can do! */
		}
	}
	setup_dialog_callbacks(did, info);
	set_timeout_avp(msg, info->interval);
	return;
}