dm_coa.c 5.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <openssl/md5.h>

#include "triton.h"
#include "log.h"

#include "radius.h"

#define PD_COA_PORT 3799

21
struct dm_coa_serv_t
22 23 24 25 26
{
	struct triton_context_t ctx;
	struct triton_md_handler_t hnd;
};

27 28
static struct dm_coa_serv_t serv;

29
static int dm_coa_check_RA(struct rad_packet_t *pack, const char *secret)
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
{
	uint8_t RA[16];
	MD5_CTX ctx;

	memset(RA, 0, 16);
	
	MD5_Init(&ctx);
	MD5_Update(&ctx, pack->buf, 4);
	MD5_Update(&ctx, RA, 16);
	MD5_Update(&ctx, pack->buf + 20, pack->len - 20);
	MD5_Update(&ctx, secret, strlen(secret));
	MD5_Final(RA, &ctx);

	return memcmp(RA, pack->buf + 4, 16);
}

46
static void dm_coa_set_RA(struct rad_packet_t *pack, const char *secret)
47 48 49 50 51 52 53 54 55
{
	MD5_CTX ctx;

	MD5_Init(&ctx);
	MD5_Update(&ctx, pack->buf, pack->len);
	MD5_Update(&ctx, secret, strlen(secret));
	MD5_Final(pack->buf + 4, &ctx);
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
static int dm_coa_send_ack(int fd, struct rad_packet_t *req, struct sockaddr_in *addr)
{
	struct rad_packet_t *reply;
	uint8_t RA[16];

	memcpy(RA, req->buf + 4, sizeof(RA));

	reply = rad_packet_alloc(req->code == CODE_COA_REQUEST ? CODE_COA_ACK : CODE_DISCONNECT_ACK);
	if (!reply)
		return -1;

	reply->id = req->id;
	
	if (rad_packet_build(reply, RA)) {
		rad_packet_free(reply);
		return -1;
	}

	dm_coa_set_RA(reply, conf_dm_coa_secret);

	if (conf_verbose) {
77 78
		log_ppp_debug("send ");
		rad_packet_print(reply, log_ppp_debug);
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
	}

	rad_packet_send(reply, fd, addr);
	
	rad_packet_free(reply);

	return 0;
}

static int dm_coa_send_nak(int fd, struct rad_packet_t *req, struct sockaddr_in *addr, int err_code)
{
	struct rad_packet_t *reply;
	uint8_t RA[16];

	memcpy(RA, req->buf + 4, sizeof(RA));

	reply = rad_packet_alloc(req->code == CODE_COA_REQUEST ? CODE_COA_NAK : CODE_DISCONNECT_NAK);
	if (!reply)
		return -1;

	reply->id = req->id;

	rad_packet_add_int(reply, "Error-Cause", err_code);

	if (rad_packet_build(reply, RA)) {
		rad_packet_free(reply);
		return -1;
	}

	dm_coa_set_RA(reply, conf_dm_coa_secret);

	if (conf_verbose) {
111 112
		log_ppp_debug("send ");
		rad_packet_print(reply, log_ppp_debug);
113 114 115 116 117 118 119 120 121 122
	}

	rad_packet_send(reply, fd, addr);
	
	rad_packet_free(reply);

	return 0;
}


123 124
static void disconnect_request(struct radius_pd_t *rpd)
{
125 126 127 128 129
	if (conf_verbose) {
		log_ppp_debug("recv ");
		rad_packet_print(rpd->dm_coa_req, log_ppp_debug);
	}

130 131
	dm_coa_send_ack(serv.hnd.fd, rpd->dm_coa_req, &rpd->dm_coa_addr);

132 133
	rad_packet_free(rpd->dm_coa_req);
	rpd->dm_coa_req = NULL;
134 135 136 137 138 139

	ppp_terminate(rpd->ppp, 0);
}

static void coa_request(struct radius_pd_t *rpd)
{
140 141 142 143
	if (conf_verbose) {
		log_ppp_debug("recv ");
		rad_packet_print(rpd->dm_coa_req, log_ppp_debug);
	}
144 145
/// TODO: CoA handling
	
146 147
	rad_packet_free(rpd->dm_coa_req);
	rpd->dm_coa_req = NULL;
148 149
}

150
static int dm_coa_read(struct triton_md_handler_t *h)
151 152 153 154 155 156 157 158 159 160 161 162
{
	struct rad_packet_t *pack;
	struct radius_pd_t *rpd;
	int err_code;
	struct sockaddr_in addr;


	pack = rad_packet_recv(h->fd, &addr);
	if (!pack)
		return 0;

	if (pack->code != CODE_DISCONNECT_REQUEST	&& pack->code != CODE_COA_REQUEST) {
163
		log_warn("radius:dm_coa: unexpected code (%i) received\n", pack->code);
164 165 166
		goto out_err_no_reply;
	}

167 168
	if (dm_coa_check_RA(pack, conf_dm_coa_secret)) {
		log_warn("radius:dm_coa: RA validation failed\n");
169 170 171 172 173 174 175 176 177
		goto out_err_no_reply;
	}

	if (conf_verbose) {
		log_debug("recv ");
		rad_packet_print(pack, log_debug);
	}

	if (rad_check_nas_pack(pack)) {
178
		log_warn("radius:dm_coa: NAS identification failed\n");
179 180 181 182 183 184
		err_code = 403;
		goto out_err;
	}
	
	rpd = rad_find_session_pack(pack);
	if (!rpd) {
185
		log_warn("radius:dm_coa: session not found\n");
186 187 188 189
		err_code = 503;
		goto out_err;
	}
	
190
	rpd->dm_coa_req = pack;
191
	memcpy(&rpd->dm_coa_addr, &addr, sizeof(addr));
192 193 194 195 196 197 198 199 200 201 202

	if (pack->code == CODE_DISCONNECT_REQUEST)
		triton_context_call(rpd->ppp->ctrl->ctx, (void (*)(void *))disconnect_request, rpd);
	else
		triton_context_call(rpd->ppp->ctrl->ctx, (void (*)(void *))coa_request, rpd);

	pthread_mutex_unlock(&rpd->lock);

	return 0;

out_err:
203
	dm_coa_send_nak(h->fd, pack, &addr, err_code);
204 205 206 207 208 209

out_err_no_reply:
	rad_packet_free(pack);
	return 0;
}

210
static void dm_coa_close(struct triton_context_t *ctx)
211
{
212
	struct dm_coa_serv_t *serv = container_of(ctx, typeof(*serv), ctx);
213 214 215 216
	triton_md_unregister_handler(&serv->hnd);
	close(serv->hnd.fd);
}

217 218
static struct dm_coa_serv_t serv = {
	.ctx.close = dm_coa_close,
219
	.ctx.before_switch = log_switch,
220
	.hnd.read = dm_coa_read,
221 222 223 224 225 226 227 228
};

static void __init init(void)
{
  struct sockaddr_in addr;

	serv.hnd.fd = socket (PF_INET, SOCK_DGRAM, 0);
  if (serv.hnd.fd < 0) {
229
    log_error("radius:dm_coa: socket: %s\n", strerror(errno));
230 231 232 233 234 235 236 237 238
    return;
  }
  addr.sin_family = AF_INET;
  addr.sin_port = htons (PD_COA_PORT);
	if (conf_nas_ip_address)
	  addr.sin_addr.s_addr = inet_addr(conf_nas_ip_address);
	else
		addr.sin_addr.s_addr = htonl (INADDR_ANY);
  if (bind (serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
239
    log_error("radius:dm_coa: bind: %s\n", strerror(errno));
240 241 242 243 244
		close(serv.hnd.fd);
    return;
  }

	if (fcntl(serv.hnd.fd, F_SETFL, O_NONBLOCK)) {
245
    log_error("radius:dm_coa: failed to set nonblocking mode: %s\n", strerror(errno));
246 247 248 249
		close(serv.hnd.fd);
    return;
	}
	
250
	triton_context_register(&serv.ctx, NULL);
251 252 253
	triton_md_register_handler(&serv.ctx, &serv.hnd);
	triton_md_enable_handler(&serv.hnd, MD_MODE_READ);
}