rtl8188eu: Add hostapd and configurationb file

Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
This commit is contained in:
Larry Finger 2013-11-27 14:23:24 -06:00
parent 7638be5ee7
commit e275d5ba1f
477 changed files with 220879 additions and 0 deletions

View file

@ -0,0 +1,8 @@
all:
@echo Nothing to be made.
clean:
rm -f *~ *.o *.d
install:
@echo Nothing to be made.

View file

@ -0,0 +1,29 @@
/*
* HTTP for WPS
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
#ifndef HTTP_H
#define HTTP_H
enum http_reply_code {
HTTP_OK = 200,
HTTP_BAD_REQUEST = 400,
UPNP_INVALID_ACTION = 401,
UPNP_INVALID_ARGS = 402,
HTTP_NOT_FOUND = 404,
HTTP_PRECONDITION_FAILED = 412,
HTTP_INTERNAL_SERVER_ERROR = 500,
HTTP_UNIMPLEMENTED = 501,
UPNP_ACTION_FAILED = 501,
UPNP_ARG_VALUE_INVALID = 600,
UPNP_ARG_VALUE_OUT_OF_RANGE = 601,
UPNP_OUT_OF_MEMORY = 603
};
#endif /* HTTP_H */

View file

@ -0,0 +1,374 @@
/*
* http_client - HTTP client
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include <fcntl.h>
#include "common.h"
#include "eloop.h"
#include "httpread.h"
#include "http_client.h"
#define HTTP_CLIENT_TIMEOUT_SEC 30
struct http_client {
struct sockaddr_in dst;
int sd;
struct wpabuf *req;
size_t req_pos;
size_t max_response;
void (*cb)(void *ctx, struct http_client *c,
enum http_client_event event);
void *cb_ctx;
struct httpread *hread;
struct wpabuf body;
};
static void http_client_timeout(void *eloop_data, void *user_ctx)
{
struct http_client *c = eloop_data;
wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
}
static void http_client_got_response(struct httpread *handle, void *cookie,
enum httpread_event e)
{
struct http_client *c = cookie;
wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
"e=%d", handle, cookie, e);
eloop_cancel_timeout(http_client_timeout, c, NULL);
switch (e) {
case HTTPREAD_EVENT_FILE_READY:
if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
{
int reply_code = httpread_reply_code_get(c->hread);
if (reply_code == 200 /* OK */) {
wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
"%s:%d",
inet_ntoa(c->dst.sin_addr),
ntohs(c->dst.sin_port));
c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
} else {
wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
"%s:%d", reply_code,
inet_ntoa(c->dst.sin_addr),
ntohs(c->dst.sin_port));
c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
}
} else
c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
break;
case HTTPREAD_EVENT_TIMEOUT:
c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
break;
case HTTPREAD_EVENT_ERROR:
c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
break;
}
}
static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
{
struct http_client *c = eloop_ctx;
int res;
wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
"bytes remaining)",
inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
(unsigned long) wpabuf_len(c->req),
(unsigned long) wpabuf_len(c->req) - c->req_pos);
res = send(c->sd, wpabuf_head(c->req) + c->req_pos,
wpabuf_len(c->req) - c->req_pos, 0);
if (res < 0) {
wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
strerror(errno));
eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
return;
}
if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
"remaining",
res, (unsigned long) wpabuf_len(c->req),
(unsigned long) wpabuf_len(c->req) - c->req_pos -
res);
c->req_pos += res;
return;
}
wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
wpabuf_free(c->req);
c->req = NULL;
c->hread = httpread_create(c->sd, http_client_got_response, c,
c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
if (c->hread == NULL) {
c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
return;
}
}
struct http_client * http_client_addr(struct sockaddr_in *dst,
struct wpabuf *req, size_t max_response,
void (*cb)(void *ctx,
struct http_client *c,
enum http_client_event event),
void *cb_ctx)
{
struct http_client *c;
c = os_zalloc(sizeof(*c));
if (c == NULL)
return NULL;
c->sd = -1;
c->dst = *dst;
c->max_response = max_response;
c->cb = cb;
c->cb_ctx = cb_ctx;
c->sd = socket(AF_INET, SOCK_STREAM, 0);
if (c->sd < 0) {
http_client_free(c);
return NULL;
}
if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
strerror(errno));
http_client_free(c);
return NULL;
}
if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
if (errno != EINPROGRESS) {
wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
strerror(errno));
http_client_free(c);
return NULL;
}
/*
* Continue connecting in the background; eloop will call us
* once the connection is ready (or failed).
*/
}
if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
c, NULL)) {
http_client_free(c);
return NULL;
}
if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
http_client_timeout, c, NULL)) {
http_client_free(c);
return NULL;
}
c->req = req;
return c;
}
char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
char **ret_path)
{
char *u, *addr, *port, *path;
u = os_strdup(url);
if (u == NULL)
return NULL;
os_memset(dst, 0, sizeof(*dst));
dst->sin_family = AF_INET;
addr = u + 7;
path = os_strchr(addr, '/');
port = os_strchr(addr, ':');
if (path == NULL) {
path = "/";
} else {
*path = '\0'; /* temporary nul termination for address */
if (port > path)
port = NULL;
}
if (port)
*port++ = '\0';
if (inet_aton(addr, &dst->sin_addr) == 0) {
/* TODO: name lookup */
wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
"(addr='%s' port='%s')",
url, addr, port);
os_free(u);
return NULL;
}
if (port)
dst->sin_port = htons(atoi(port));
else
dst->sin_port = htons(80);
if (*path == '\0') {
/* remove temporary nul termination for address */
*path = '/';
}
*ret_path = path;
return u;
}
struct http_client * http_client_url(const char *url,
struct wpabuf *req, size_t max_response,
void (*cb)(void *ctx,
struct http_client *c,
enum http_client_event event),
void *cb_ctx)
{
struct sockaddr_in dst;
struct http_client *c;
char *u, *path;
struct wpabuf *req_buf = NULL;
if (os_strncmp(url, "http://", 7) != 0)
return NULL;
u = http_client_url_parse(url, &dst, &path);
if (u == NULL)
return NULL;
if (req == NULL) {
req_buf = wpabuf_alloc(os_strlen(url) + 1000);
if (req_buf == NULL) {
os_free(u);
return NULL;
}
req = req_buf;
wpabuf_printf(req,
"GET %s HTTP/1.1\r\n"
"Cache-Control: no-cache\r\n"
"Pragma: no-cache\r\n"
"Accept: text/xml, application/xml\r\n"
"User-Agent: wpa_supplicant\r\n"
"Host: %s:%d\r\n"
"\r\n",
path, inet_ntoa(dst.sin_addr),
ntohs(dst.sin_port));
}
os_free(u);
c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
if (c == NULL) {
wpabuf_free(req_buf);
return NULL;
}
return c;
}
void http_client_free(struct http_client *c)
{
if (c == NULL)
return;
httpread_destroy(c->hread);
wpabuf_free(c->req);
if (c->sd >= 0) {
eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
close(c->sd);
}
eloop_cancel_timeout(http_client_timeout, c, NULL);
os_free(c);
}
struct wpabuf * http_client_get_body(struct http_client *c)
{
if (c->hread == NULL)
return NULL;
wpabuf_set(&c->body, httpread_data_get(c->hread),
httpread_length_get(c->hread));
return &c->body;
}
char * http_client_get_hdr_line(struct http_client *c, const char *tag)
{
if (c->hread == NULL)
return NULL;
return httpread_hdr_line_get(c->hread, tag);
}
char * http_link_update(char *url, const char *base)
{
char *n;
size_t len;
const char *pos;
/* RFC 2396, Chapter 5.2 */
/* TODO: consider adding all cases described in RFC 2396 */
if (url == NULL)
return NULL;
if (os_strncmp(url, "http://", 7) == 0)
return url; /* absolute link */
if (os_strncmp(base, "http://", 7) != 0)
return url; /* unable to handle base URL */
len = os_strlen(url) + 1 + os_strlen(base) + 1;
n = os_malloc(len);
if (n == NULL)
return url; /* failed */
if (url[0] == '/') {
pos = os_strchr(base + 7, '/');
if (pos == NULL) {
os_snprintf(n, len, "%s%s", base, url);
} else {
os_memcpy(n, base, pos - base);
os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
}
} else {
pos = os_strrchr(base + 7, '/');
if (pos == NULL) {
os_snprintf(n, len, "%s/%s", base, url);
} else {
os_memcpy(n, base, pos - base + 1);
os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
1);
}
}
os_free(url);
return n;
}

View file

@ -0,0 +1,46 @@
/*
* http_client - HTTP client
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef HTTP_CLIENT_H
#define HTTP_CLIENT_H
struct http_client;
enum http_client_event {
HTTP_CLIENT_FAILED,
HTTP_CLIENT_TIMEOUT,
HTTP_CLIENT_OK,
HTTP_CLIENT_INVALID_REPLY,
};
char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
char **path);
struct http_client * http_client_addr(struct sockaddr_in *dst,
struct wpabuf *req, size_t max_response,
void (*cb)(void *ctx,
struct http_client *c,
enum http_client_event event),
void *cb_ctx);
struct http_client * http_client_url(const char *url,
struct wpabuf *req, size_t max_response,
void (*cb)(void *ctx,
struct http_client *c,
enum http_client_event event),
void *cb_ctx);
void http_client_free(struct http_client *c);
struct wpabuf * http_client_get_body(struct http_client *c);
char * http_client_get_hdr_line(struct http_client *c, const char *tag);
char * http_link_update(char *url, const char *base);
#endif /* HTTP_CLIENT_H */

View file

@ -0,0 +1,312 @@
/*
* http_server - HTTP server
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include <fcntl.h>
#include "common.h"
#include "eloop.h"
#include "httpread.h"
#include "http_server.h"
#define HTTP_SERVER_TIMEOUT 30
#define HTTP_SERVER_MAX_REQ_LEN 8000
#define HTTP_SERVER_MAX_CONNECTIONS 10
struct http_request {
struct http_request *next;
struct http_server *srv;
int fd;
struct sockaddr_in cli;
struct httpread *hread;
};
struct http_server {
void (*cb)(void *ctx, struct http_request *req);
void *cb_ctx;
int fd;
int port;
struct http_request *requests;
unsigned int request_count;
};
static void http_request_cb(struct httpread *handle, void *cookie,
enum httpread_event en)
{
struct http_request *req = cookie;
struct http_server *srv = req->srv;
if (en == HTTPREAD_EVENT_FILE_READY) {
wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received",
inet_ntoa(req->cli.sin_addr),
ntohs(req->cli.sin_port));
srv->cb(srv->cb_ctx, req);
return;
}
wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received "
"completely", inet_ntoa(req->cli.sin_addr),
ntohs(req->cli.sin_port));
http_request_deinit(req);
}
static struct http_request * http_request_init(struct http_server *srv, int fd,
struct sockaddr_in *cli)
{
struct http_request *req;
if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) {
wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests");
return NULL;
}
req = os_zalloc(sizeof(*req));
if (req == NULL)
return NULL;
req->srv = srv;
req->fd = fd;
req->cli = *cli;
req->hread = httpread_create(req->fd, http_request_cb, req,
HTTP_SERVER_MAX_REQ_LEN,
HTTP_SERVER_TIMEOUT);
if (req->hread == NULL) {
http_request_deinit(req);
return NULL;
}
return req;
}
void http_request_deinit(struct http_request *req)
{
struct http_request *r, *p;
struct http_server *srv;
if (req == NULL)
return;
srv = req->srv;
p = NULL;
r = srv->requests;
while (r) {
if (r == req) {
if (p)
p->next = r->next;
else
srv->requests = r->next;
srv->request_count--;
break;
}
p = r;
r = r->next;
}
httpread_destroy(req->hread);
close(req->fd);
os_free(req);
}
static void http_request_free_all(struct http_request *req)
{
struct http_request *prev;
while (req) {
prev = req;
req = req->next;
http_request_deinit(prev);
}
}
void http_request_send(struct http_request *req, struct wpabuf *resp)
{
int res;
wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d",
(unsigned long) wpabuf_len(resp),
inet_ntoa(req->cli.sin_addr),
ntohs(req->cli.sin_port));
res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0);
if (res < 0) {
wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s",
strerror(errno));
} else if ((size_t) res < wpabuf_len(resp)) {
wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes",
res, (unsigned long) wpabuf_len(resp));
/* TODO: add eloop handler for sending rest of the data */
}
wpabuf_free(resp);
}
void http_request_send_and_deinit(struct http_request *req,
struct wpabuf *resp)
{
http_request_send(req, resp);
http_request_deinit(req);
}
enum httpread_hdr_type http_request_get_type(struct http_request *req)
{
return httpread_hdr_type_get(req->hread);
}
char * http_request_get_uri(struct http_request *req)
{
return httpread_uri_get(req->hread);
}
char * http_request_get_hdr(struct http_request *req)
{
return httpread_hdr_get(req->hread);
}
char * http_request_get_data(struct http_request *req)
{
return httpread_data_get(req->hread);
}
char * http_request_get_hdr_line(struct http_request *req, const char *tag)
{
return httpread_hdr_line_get(req->hread, tag);
}
struct sockaddr_in * http_request_get_cli_addr(struct http_request *req)
{
return &req->cli;
}
static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx)
{
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
struct http_server *srv = eloop_ctx;
int conn;
struct http_request *req;
conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len);
if (conn < 0) {
wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: "
"%s", strerror(errno));
return;
}
wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
req = http_request_init(srv, conn, &addr);
if (req == NULL) {
close(conn);
return;
}
req->next = srv->requests;
srv->requests = req;
srv->request_count++;
}
struct http_server * http_server_init(struct in_addr *addr, int port,
void (*cb)(void *ctx,
struct http_request *req),
void *cb_ctx)
{
struct sockaddr_in sin;
struct http_server *srv;
srv = os_zalloc(sizeof(*srv));
if (srv == NULL)
return NULL;
srv->cb = cb;
srv->cb_ctx = cb_ctx;
srv->fd = socket(AF_INET, SOCK_STREAM, 0);
if (srv->fd < 0)
goto fail;
if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
goto fail;
if (port < 0)
srv->port = 49152;
else
srv->port = port;
os_memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = addr->s_addr;
for (;;) {
sin.sin_port = htons(srv->port);
if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0)
break;
if (errno == EADDRINUSE) {
/* search for unused port */
if (++srv->port == 65535 || port >= 0)
goto fail;
continue;
}
wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: "
"%s", srv->port, strerror(errno));
goto fail;
}
if (listen(srv->fd, 10 /* max backlog */) < 0)
goto fail;
if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
goto fail;
if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
srv, NULL))
goto fail;
wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d",
inet_ntoa(*addr), srv->port);
return srv;
fail:
http_server_deinit(srv);
return NULL;
}
void http_server_deinit(struct http_server *srv)
{
if (srv == NULL)
return;
if (srv->fd >= 0) {
eloop_unregister_sock(srv->fd, EVENT_TYPE_READ);
close(srv->fd);
}
http_request_free_all(srv->requests);
os_free(srv);
}
int http_server_get_port(struct http_server *srv)
{
return srv->port;
}

View file

@ -0,0 +1,39 @@
/*
* http_server - HTTP server
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef HTTP_SERVER_H
#define HTTP_SERVER_H
struct http_server;
struct http_request;
void http_request_deinit(struct http_request *req);
void http_request_send(struct http_request *req, struct wpabuf *resp);
void http_request_send_and_deinit(struct http_request *req,
struct wpabuf *resp);
enum httpread_hdr_type http_request_get_type(struct http_request *req);
char * http_request_get_uri(struct http_request *req);
char * http_request_get_hdr(struct http_request *req);
char * http_request_get_data(struct http_request *req);
char * http_request_get_hdr_line(struct http_request *req, const char *tag);
struct sockaddr_in * http_request_get_cli_addr(struct http_request *req);
struct http_server * http_server_init(struct in_addr *addr, int port,
void (*cb)(void *ctx,
struct http_request *req),
void *cb_ctx);
void http_server_deinit(struct http_server *srv);
int http_server_get_port(struct http_server *srv);
#endif /* HTTP_SERVER_H */

View file

@ -0,0 +1,861 @@
/*
* httpread - Manage reading file(s) from HTTP/TCP socket
* Author: Ted Merrill
* Copyright 2008 Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*
* The files are buffered via internal callbacks from eloop, then presented to
* an application callback routine when completely read into memory. May also
* be used if no file is expected but just to get the header, including HTTP
* replies (e.g. HTTP/1.1 200 OK etc.).
*
* This does not attempt to be an optimally efficient implementation, but does
* attempt to be of reasonably small size and memory consumption; assuming that
* only small files are to be read. A maximum file size is provided by
* application and enforced.
*
* It is assumed that the application does not expect any of the following:
* -- transfer encoding other than chunked
* -- trailer fields
* It is assumed that, even if the other side requested that the connection be
* kept open, that we will close it (thus HTTP messages sent by application
* should have the connection closed field); this is allowed by HTTP/1.1 and
* simplifies things for us.
*
* Other limitations:
* -- HTTP header may not exceed a hard-coded size.
*
* Notes:
* This code would be massively simpler without some of the new features of
* HTTP/1.1, especially chunked data.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "httpread.h"
/* Tunable parameters */
#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */
#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */
#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */
#if 0
/* httpread_debug -- set this global variable > 0 e.g. from debugger
* to enable debugs (larger numbers for more debugs)
* Make this a #define of 0 to eliminate the debugging code.
*/
int httpread_debug = 99;
#else
#define httpread_debug 0 /* eliminates even the debugging code */
#endif
/* control instance -- actual definition (opaque to application)
*/
struct httpread {
/* information from creation */
int sd; /* descriptor of TCP socket to read from */
void (*cb)(struct httpread *handle, void *cookie,
enum httpread_event e); /* call on event */
void *cookie; /* pass to callback */
int max_bytes; /* maximum file size else abort it */
int timeout_seconds; /* 0 or total duration timeout period */
/* dynamically used information follows */
int sd_registered; /* nonzero if we need to unregister socket */
int to_registered; /* nonzero if we need to unregister timeout */
int got_hdr; /* nonzero when header is finalized */
char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
int hdr_nbytes;
enum httpread_hdr_type hdr_type;
int version; /* 1 if we've seen 1.1 */
int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
int got_content_length; /* true if we know content length for sure */
int content_length; /* body length, iff got_content_length */
int chunked; /* nonzero for chunked data */
char *uri;
int got_body; /* nonzero when body is finalized */
char *body;
int body_nbytes;
int body_alloc_nbytes; /* amount allocated */
int got_file; /* here when we are done */
/* The following apply if data is chunked: */
int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/
int chunk_start; /* offset in body of chunk hdr or data */
int chunk_size; /* data of chunk (not hdr or ending CRLF)*/
int in_trailer; /* in header fields after data (chunked only)*/
enum trailer_state {
trailer_line_begin = 0,
trailer_empty_cr, /* empty line + CR */
trailer_nonempty,
trailer_nonempty_cr,
} trailer_state;
};
/* Check words for equality, where words consist of graphical characters
* delimited by whitespace
* Returns nonzero if "equal" doing case insensitive comparison.
*/
static int word_eq(char *s1, char *s2)
{
int c1;
int c2;
int end1 = 0;
int end2 = 0;
for (;;) {
c1 = *s1++;
c2 = *s2++;
if (isalpha(c1) && isupper(c1))
c1 = tolower(c1);
if (isalpha(c2) && isupper(c2))
c2 = tolower(c2);
end1 = !isgraph(c1);
end2 = !isgraph(c2);
if (end1 || end2 || c1 != c2)
break;
}
return end1 && end2; /* reached end of both words? */
}
/* convert hex to binary
* Requires that c have been previously tested true with isxdigit().
*/
static int hex_value(int c)
{
if (isdigit(c))
return c - '0';
if (islower(c))
return 10 + c - 'a';
return 10 + c - 'A';
}
static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
/* httpread_destroy -- if h is non-NULL, clean up
* This must eventually be called by the application following
* call of the application's callback and may be called
* earlier if desired.
*/
void httpread_destroy(struct httpread *h)
{
if (httpread_debug >= 10)
wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
if (!h)
return;
if (h->to_registered)
eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
h->to_registered = 0;
if (h->sd_registered)
eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
h->sd_registered = 0;
os_free(h->body);
os_free(h->uri);
os_memset(h, 0, sizeof(*h)); /* aid debugging */
h->sd = -1; /* aid debugging */
os_free(h);
}
/* httpread_timeout_handler -- called on excessive total duration
*/
static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
{
struct httpread *h = user_ctx;
wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
h->to_registered = 0; /* is self-cancelling */
(*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
}
/* Analyze options only so far as is needed to correctly obtain the file.
* The application can look at the raw header to find other options.
*/
static int httpread_hdr_option_analyze(
struct httpread *h,
char *hbp /* pointer to current line in header buffer */
)
{
if (word_eq(hbp, "CONTENT-LENGTH:")) {
while (isgraph(*hbp))
hbp++;
while (*hbp == ' ' || *hbp == '\t')
hbp++;
if (!isdigit(*hbp))
return -1;
h->content_length = atol(hbp);
h->got_content_length = 1;
return 0;
}
if (word_eq(hbp, "TRANSFER_ENCODING:") ||
word_eq(hbp, "TRANSFER-ENCODING:")) {
while (isgraph(*hbp))
hbp++;
while (*hbp == ' ' || *hbp == '\t')
hbp++;
/* There should (?) be no encodings of interest
* other than chunked...
*/
if (word_eq(hbp, "CHUNKED")) {
h->chunked = 1;
h->in_chunk_data = 0;
/* ignore possible ;<parameters> */
}
return 0;
}
/* skip anything we don't know, which is a lot */
return 0;
}
static int httpread_hdr_analyze(struct httpread *h)
{
char *hbp = h->hdr; /* pointer into h->hdr */
int standard_first_line = 1;
/* First line is special */
h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
if (!isgraph(*hbp))
goto bad;
if (os_strncmp(hbp, "HTTP/", 5) == 0) {
h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
standard_first_line = 0;
hbp += 5;
if (hbp[0] == '1' && hbp[1] == '.' &&
isdigit(hbp[2]) && hbp[2] != '0')
h->version = 1;
while (isgraph(*hbp))
hbp++;
while (*hbp == ' ' || *hbp == '\t')
hbp++;
if (!isdigit(*hbp))
goto bad;
h->reply_code = atol(hbp);
} else if (word_eq(hbp, "GET"))
h->hdr_type = HTTPREAD_HDR_TYPE_GET;
else if (word_eq(hbp, "HEAD"))
h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
else if (word_eq(hbp, "POST"))
h->hdr_type = HTTPREAD_HDR_TYPE_POST;
else if (word_eq(hbp, "PUT"))
h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
else if (word_eq(hbp, "DELETE"))
h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
else if (word_eq(hbp, "TRACE"))
h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
else if (word_eq(hbp, "CONNECT"))
h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
else if (word_eq(hbp, "NOTIFY"))
h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
else if (word_eq(hbp, "M-SEARCH"))
h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
else if (word_eq(hbp, "M-POST"))
h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
else if (word_eq(hbp, "SUBSCRIBE"))
h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
else if (word_eq(hbp, "UNSUBSCRIBE"))
h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
else {
}
if (standard_first_line) {
char *rawuri;
char *uri;
/* skip type */
while (isgraph(*hbp))
hbp++;
while (*hbp == ' ' || *hbp == '\t')
hbp++;
/* parse uri.
* Find length, allocate memory for translated
* copy, then translate by changing %<hex><hex>
* into represented value.
*/
rawuri = hbp;
while (isgraph(*hbp))
hbp++;
h->uri = os_malloc((hbp - rawuri) + 1);
if (h->uri == NULL)
goto bad;
uri = h->uri;
while (rawuri < hbp) {
int c = *rawuri;
if (c == '%' &&
isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
*uri++ = (hex_value(rawuri[1]) << 4) |
hex_value(rawuri[2]);
rawuri += 3;
} else {
*uri++ = c;
rawuri++;
}
}
*uri = 0; /* null terminate */
while (isgraph(*hbp))
hbp++;
while (*hbp == ' ' || *hbp == '\t')
hbp++;
/* get version */
if (0 == strncmp(hbp, "HTTP/", 5)) {
hbp += 5;
if (hbp[0] == '1' && hbp[1] == '.' &&
isdigit(hbp[2]) && hbp[2] != '0')
h->version = 1;
}
}
/* skip rest of line */
while (*hbp)
if (*hbp++ == '\n')
break;
/* Remainder of lines are options, in any order;
* or empty line to terminate
*/
for (;;) {
/* Empty line to terminate */
if (hbp[0] == '\n' ||
(hbp[0] == '\r' && hbp[1] == '\n'))
break;
if (!isgraph(*hbp))
goto bad;
if (httpread_hdr_option_analyze(h, hbp))
goto bad;
/* skip line */
while (*hbp)
if (*hbp++ == '\n')
break;
}
/* chunked overrides content-length always */
if (h->chunked)
h->got_content_length = 0;
/* For some types, we should not try to read a body
* This is in addition to the application determining
* that we should not read a body.
*/
switch (h->hdr_type) {
case HTTPREAD_HDR_TYPE_REPLY:
/* Some codes can have a body and some not.
* For now, just assume that any other than 200
* do not...
*/
if (h->reply_code != 200)
h->max_bytes = 0;
break;
case HTTPREAD_HDR_TYPE_GET:
case HTTPREAD_HDR_TYPE_HEAD:
/* in practice it appears that it is assumed
* that GETs have a body length of 0... ?
*/
if (h->chunked == 0 && h->got_content_length == 0)
h->max_bytes = 0;
break;
case HTTPREAD_HDR_TYPE_POST:
case HTTPREAD_HDR_TYPE_PUT:
case HTTPREAD_HDR_TYPE_DELETE:
case HTTPREAD_HDR_TYPE_TRACE:
case HTTPREAD_HDR_TYPE_CONNECT:
case HTTPREAD_HDR_TYPE_NOTIFY:
case HTTPREAD_HDR_TYPE_M_SEARCH:
case HTTPREAD_HDR_TYPE_M_POST:
case HTTPREAD_HDR_TYPE_SUBSCRIBE:
case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
default:
break;
}
return 0;
bad:
/* Error */
return -1;
}
/* httpread_read_handler -- called when socket ready to read
*
* Note: any extra data we read past end of transmitted file is ignored;
* if we were to support keeping connections open for multiple files then
* this would have to be addressed.
*/
static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
{
struct httpread *h = sock_ctx;
int nread;
char *rbp; /* pointer into read buffer */
char *hbp; /* pointer into header buffer */
char *bbp; /* pointer into body buffer */
char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
if (httpread_debug >= 20)
wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
/* read some at a time, then search for the interal
* boundaries between header and data and etc.
*/
nread = read(h->sd, readbuf, sizeof(readbuf));
if (nread < 0)
goto bad;
if (nread == 0) {
/* end of transmission... this may be normal
* or may be an error... in some cases we can't
* tell which so we must assume it is normal then.
*/
if (!h->got_hdr) {
/* Must at least have completed header */
wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
goto bad;
}
if (h->chunked || h->got_content_length) {
/* Premature EOF; e.g. dropped connection */
wpa_printf(MSG_DEBUG,
"httpread premature eof(%p) %d/%d",
h, h->body_nbytes,
h->content_length);
goto bad;
}
/* No explicit length, hopefully we have all the data
* although dropped connections can cause false
* end
*/
if (httpread_debug >= 10)
wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
h->got_body = 1;
goto got_file;
}
rbp = readbuf;
/* Header consists of text lines (terminated by both CR and LF)
* and an empty line (CR LF only).
*/
if (!h->got_hdr) {
hbp = h->hdr + h->hdr_nbytes;
/* add to headers until:
* -- we run out of data in read buffer
* -- or, we run out of header buffer room
* -- or, we get double CRLF in headers
*/
for (;;) {
if (nread == 0)
goto get_more;
if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
goto bad;
}
*hbp++ = *rbp++;
nread--;
h->hdr_nbytes++;
if (h->hdr_nbytes >= 4 &&
hbp[-1] == '\n' &&
hbp[-2] == '\r' &&
hbp[-3] == '\n' &&
hbp[-4] == '\r' ) {
h->got_hdr = 1;
*hbp = 0; /* null terminate */
break;
}
}
/* here we've just finished reading the header */
if (httpread_hdr_analyze(h)) {
wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
goto bad;
}
if (h->max_bytes == 0) {
if (httpread_debug >= 10)
wpa_printf(MSG_DEBUG,
"httpread no body hdr end(%p)", h);
goto got_file;
}
if (h->got_content_length && h->content_length == 0) {
if (httpread_debug >= 10)
wpa_printf(MSG_DEBUG,
"httpread zero content length(%p)",
h);
goto got_file;
}
}
/* Certain types of requests never have data and so
* must be specially recognized.
*/
if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
!os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
!os_strncasecmp(h->hdr, "HEAD", 4) ||
!os_strncasecmp(h->hdr, "GET", 3)) {
if (!h->got_body) {
if (httpread_debug >= 10)
wpa_printf(MSG_DEBUG,
"httpread NO BODY for sp. type");
}
h->got_body = 1;
goto got_file;
}
/* Data can be just plain binary data, or if "chunked"
* consists of chunks each with a header, ending with
* an ending header.
*/
if (nread == 0)
goto get_more;
if (!h->got_body) {
/* Here to get (more of) body */
/* ensure we have enough room for worst case for body
* plus a null termination character
*/
if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
char *new_body;
int new_alloc_nbytes;
if (h->body_nbytes >= h->max_bytes)
goto bad;
new_alloc_nbytes = h->body_alloc_nbytes +
HTTPREAD_BODYBUF_DELTA;
/* For content-length case, the first time
* through we allocate the whole amount
* we need.
*/
if (h->got_content_length &&
new_alloc_nbytes < (h->content_length + 1))
new_alloc_nbytes = h->content_length + 1;
if ((new_body = os_realloc(h->body, new_alloc_nbytes))
== NULL)
goto bad;
h->body = new_body;
h->body_alloc_nbytes = new_alloc_nbytes;
}
/* add bytes */
bbp = h->body + h->body_nbytes;
for (;;) {
int ncopy;
/* See if we need to stop */
if (h->chunked && h->in_chunk_data == 0) {
/* in chunk header */
char *cbp = h->body + h->chunk_start;
if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
bbp[-1] == '\n') {
/* end of chunk hdr line */
/* hdr line consists solely
* of a hex numeral and CFLF
*/
if (!isxdigit(*cbp))
goto bad;
h->chunk_size = strtoul(cbp, NULL, 16);
/* throw away chunk header
* so we have only real data
*/
h->body_nbytes = h->chunk_start;
bbp = cbp;
if (h->chunk_size == 0) {
/* end of chunking */
/* trailer follows */
h->in_trailer = 1;
if (httpread_debug >= 20)
wpa_printf(
MSG_DEBUG,
"httpread end chunks(%p)", h);
break;
}
h->in_chunk_data = 1;
/* leave chunk_start alone */
}
} else if (h->chunked) {
/* in chunk data */
if ((h->body_nbytes - h->chunk_start) ==
(h->chunk_size + 2)) {
/* end of chunk reached,
* new chunk starts
*/
/* check chunk ended w/ CRLF
* which we'll throw away
*/
if (bbp[-1] == '\n' &&
bbp[-2] == '\r') {
} else
goto bad;
h->body_nbytes -= 2;
bbp -= 2;
h->chunk_start = h->body_nbytes;
h->in_chunk_data = 0;
h->chunk_size = 0; /* just in case */
}
} else if (h->got_content_length &&
h->body_nbytes >= h->content_length) {
h->got_body = 1;
if (httpread_debug >= 10)
wpa_printf(
MSG_DEBUG,
"httpread got content(%p)", h);
goto got_file;
}
if (nread <= 0)
break;
/* Now transfer. Optimize using memcpy where we can. */
if (h->chunked && h->in_chunk_data) {
/* copy up to remainder of chunk data
* plus the required CR+LF at end
*/
ncopy = (h->chunk_start + h->chunk_size + 2) -
h->body_nbytes;
} else if (h->chunked) {
/*in chunk header -- don't optimize */
*bbp++ = *rbp++;
nread--;
h->body_nbytes++;
continue;
} else if (h->got_content_length) {
ncopy = h->content_length - h->body_nbytes;
} else {
ncopy = nread;
}
/* Note: should never be 0 */
if (ncopy > nread)
ncopy = nread;
os_memcpy(bbp, rbp, ncopy);
bbp += ncopy;
h->body_nbytes += ncopy;
rbp += ncopy;
nread -= ncopy;
} /* body copy loop */
} /* !got_body */
if (h->chunked && h->in_trailer) {
/* If "chunked" then there is always a trailer,
* consisting of zero or more non-empty lines
* ending with CR LF and then an empty line w/ CR LF.
* We do NOT support trailers except to skip them --
* this is supported (generally) by the http spec.
*/
bbp = h->body + h->body_nbytes;
for (;;) {
int c;
if (nread <= 0)
break;
c = *rbp++;
nread--;
switch (h->trailer_state) {
case trailer_line_begin:
if (c == '\r')
h->trailer_state = trailer_empty_cr;
else
h->trailer_state = trailer_nonempty;
break;
case trailer_empty_cr:
/* end empty line */
if (c == '\n') {
h->trailer_state = trailer_line_begin;
h->in_trailer = 0;
if (httpread_debug >= 10)
wpa_printf(
MSG_DEBUG,
"httpread got content(%p)", h);
h->got_body = 1;
goto got_file;
}
h->trailer_state = trailer_nonempty;
break;
case trailer_nonempty:
if (c == '\r')
h->trailer_state = trailer_nonempty_cr;
break;
case trailer_nonempty_cr:
if (c == '\n')
h->trailer_state = trailer_line_begin;
else
h->trailer_state = trailer_nonempty;
break;
}
}
}
goto get_more;
bad:
/* Error */
wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
(*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
return;
get_more:
return;
got_file:
if (httpread_debug >= 10)
wpa_printf(MSG_DEBUG,
"httpread got file %d bytes type %d",
h->body_nbytes, h->hdr_type);
/* Null terminate for convenience of some applications */
if (h->body)
h->body[h->body_nbytes] = 0; /* null terminate */
h->got_file = 1;
/* Assume that we do NOT support keeping connection alive,
* and just in case somehow we don't get destroyed right away,
* unregister now.
*/
if (h->sd_registered)
eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
h->sd_registered = 0;
/* The application can destroy us whenever they feel like...
* cancel timeout.
*/
if (h->to_registered)
eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
h->to_registered = 0;
(*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
}
/* httpread_create -- start a new reading session making use of eloop.
* The new instance will use the socket descriptor for reading (until
* it gets a file and not after) but will not close the socket, even
* when the instance is destroyed (the application must do that).
* Return NULL on error.
*
* Provided that httpread_create successfully returns a handle,
* the callback fnc is called to handle httpread_event events.
* The caller should do destroy on any errors or unknown events.
*
* Pass max_bytes == 0 to not read body at all (required for e.g.
* reply to HEAD request).
*/
struct httpread * httpread_create(
int sd, /* descriptor of TCP socket to read from */
void (*cb)(struct httpread *handle, void *cookie,
enum httpread_event e), /* call on event */
void *cookie, /* pass to callback */
int max_bytes, /* maximum body size else abort it */
int timeout_seconds /* 0; or total duration timeout period */
)
{
struct httpread *h = NULL;
h = os_zalloc(sizeof(*h));
if (h == NULL)
goto fail;
h->sd = sd;
h->cb = cb;
h->cookie = cookie;
h->max_bytes = max_bytes;
h->timeout_seconds = timeout_seconds;
if (timeout_seconds > 0) {
if (eloop_register_timeout(timeout_seconds, 0,
httpread_timeout_handler,
NULL, h)) {
/* No way to recover (from malloc failure) */
goto fail;
}
h->to_registered = 1;
}
if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
NULL, h)) {
/* No way to recover (from malloc failure) */
goto fail;
}
h->sd_registered = 1;
return h;
fail:
/* Error */
httpread_destroy(h);
return NULL;
}
/* httpread_hdr_type_get -- When file is ready, returns header type. */
enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
{
return h->hdr_type;
}
/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
* or possibly NULL (which would be an error).
*/
char * httpread_uri_get(struct httpread *h)
{
return h->uri;
}
/* httpread_reply_code_get -- When reply is ready, returns reply code */
int httpread_reply_code_get(struct httpread *h)
{
return h->reply_code;
}
/* httpread_length_get -- When file is ready, returns file length. */
int httpread_length_get(struct httpread *h)
{
return h->body_nbytes;
}
/* httpread_data_get -- When file is ready, returns file content
* with null byte appened.
* Might return NULL in some error condition.
*/
void * httpread_data_get(struct httpread *h)
{
return h->body ? h->body : "";
}
/* httpread_hdr_get -- When file is ready, returns header content
* with null byte appended.
* Might return NULL in some error condition.
*/
char * httpread_hdr_get(struct httpread *h)
{
return h->hdr;
}
/* httpread_hdr_line_get -- When file is ready, returns pointer
* to line within header content matching the given tag
* (after the tag itself and any spaces/tabs).
*
* The tag should end with a colon for reliable matching.
*
* If not found, returns NULL;
*/
char * httpread_hdr_line_get(struct httpread *h, const char *tag)
{
int tag_len = os_strlen(tag);
char *hdr = h->hdr;
hdr = os_strchr(hdr, '\n');
if (hdr == NULL)
return NULL;
hdr++;
for (;;) {
if (!os_strncasecmp(hdr, tag, tag_len)) {
hdr += tag_len;
while (*hdr == ' ' || *hdr == '\t')
hdr++;
return hdr;
}
hdr = os_strchr(hdr, '\n');
if (hdr == NULL)
return NULL;
hdr++;
}
}

View file

@ -0,0 +1,123 @@
/*
* httpread - Manage reading file(s) from HTTP/TCP socket
* Author: Ted Merrill
* Copyright 2008 Atheros Communications
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef HTTPREAD_H
#define HTTPREAD_H
/* event types (passed to callback) */
enum httpread_event {
HTTPREAD_EVENT_FILE_READY = 1, /* including reply ready */
HTTPREAD_EVENT_TIMEOUT = 2,
HTTPREAD_EVENT_ERROR = 3 /* misc. error, esp malloc error */
};
/* header type detected
* available to callback via call to httpread_reply_code_get()
*/
enum httpread_hdr_type {
HTTPREAD_HDR_TYPE_UNKNOWN = 0, /* none of the following */
HTTPREAD_HDR_TYPE_REPLY = 1, /* hdr begins w/ HTTP/ */
HTTPREAD_HDR_TYPE_GET = 2, /* hdr begins with GET<sp> */
HTTPREAD_HDR_TYPE_HEAD = 3, /* hdr begins with HEAD<sp> */
HTTPREAD_HDR_TYPE_POST = 4, /* hdr begins with POST<sp> */
HTTPREAD_HDR_TYPE_PUT = 5, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_DELETE = 6, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_TRACE = 7, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_CONNECT = 8, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_NOTIFY = 9, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_M_SEARCH = 10, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_M_POST = 11, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_SUBSCRIBE = 12, /* hdr begins with ... */
HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */
HTTPREAD_N_HDR_TYPES /* keep last */
};
/* control instance -- opaque struct declaration
*/
struct httpread;
/* httpread_destroy -- if h is non-NULL, clean up
* This must eventually be called by the application following
* call of the application's callback and may be called
* earlier if desired.
*/
void httpread_destroy(struct httpread *h);
/* httpread_create -- start a new reading session making use of eloop.
* The new instance will use the socket descriptor for reading (until
* it gets a file and not after) but will not close the socket, even
* when the instance is destroyed (the application must do that).
* Return NULL on error.
*
* Provided that httpread_create successfully returns a handle,
* the callback fnc is called to handle httpread_event events.
* The caller should do destroy on any errors or unknown events.
*
* Pass max_bytes == 0 to not read body at all (required for e.g.
* reply to HEAD request).
*/
struct httpread * httpread_create(
int sd, /* descriptor of TCP socket to read from */
void (*cb)(struct httpread *handle, void *cookie,
enum httpread_event e), /* call on event */
void *cookie, /* pass to callback */
int max_bytes, /* maximum file size else abort it */
int timeout_seconds /* 0; or total duration timeout period */
);
/* httpread_hdr_type_get -- When file is ready, returns header type.
*/
enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h);
/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
* or possibly NULL (which would be an error).
*/
char *httpread_uri_get(struct httpread *h);
/* httpread_reply_code_get -- When reply is ready, returns reply code */
int httpread_reply_code_get(struct httpread *h);
/* httpread_length_get -- When file is ready, returns file length. */
int httpread_length_get(struct httpread *h);
/* httpread_data_get -- When file is ready, returns file content
* with null byte appened.
* Might return NULL in some error condition.
*/
void * httpread_data_get(struct httpread *h);
/* httpread_hdr_get -- When file is ready, returns header content
* with null byte appended.
* Might return NULL in some error condition.
*/
char * httpread_hdr_get(struct httpread *h);
/* httpread_hdr_line_get -- When file is ready, returns pointer
* to line within header content matching the given tag
* (after the tag itself and any spaces/tabs).
*
* The tag should end with a colon for reliable matching.
*
* If not found, returns NULL;
*/
char * httpread_hdr_line_get(struct httpread *h, const char *tag);
#endif /* HTTPREAD_H */

175
hostapd-0.8/src/wps/ndef.c Normal file
View file

@ -0,0 +1,175 @@
/*
* NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
* Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
* Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "wps/wps.h"
#include "wps/wps_i.h"
#define FLAG_MESSAGE_BEGIN (1 << 7)
#define FLAG_MESSAGE_END (1 << 6)
#define FLAG_CHUNK (1 << 5)
#define FLAG_SHORT_RECORD (1 << 4)
#define FLAG_ID_LENGTH_PRESENT (1 << 3)
#define FLAG_TNF_RFC2046 (0x02)
struct ndef_record {
u8 *type;
u8 *id;
u8 *payload;
u8 type_length;
u8 id_length;
u32 payload_length;
u32 total_length;
};
static char wifi_handover_type[] = "application/vnd.wfa.wsc";
static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
{
u8 *pos = data + 1;
if (size < 2)
return -1;
record->type_length = *pos++;
if (data[0] & FLAG_SHORT_RECORD) {
if (size < 3)
return -1;
record->payload_length = *pos++;
} else {
if (size < 6)
return -1;
record->payload_length = ntohl(*(u32 *)pos);
pos += sizeof(u32);
}
if (data[0] & FLAG_ID_LENGTH_PRESENT) {
if ((int) size < pos - data + 1)
return -1;
record->id_length = *pos++;
} else
record->id_length = 0;
record->type = record->type_length == 0 ? NULL : pos;
pos += record->type_length;
record->id = record->id_length == 0 ? NULL : pos;
pos += record->id_length;
record->payload = record->payload_length == 0 ? NULL : pos;
pos += record->payload_length;
record->total_length = pos - data;
if (record->total_length > size)
return -1;
return 0;
}
static struct wpabuf * ndef_parse_records(struct wpabuf *buf,
int (*filter)(struct ndef_record *))
{
struct ndef_record record;
int len = wpabuf_len(buf);
u8 *data = wpabuf_mhead(buf);
while (len > 0) {
if (ndef_parse_record(data, len, &record) < 0) {
wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
return NULL;
}
if (filter == NULL || filter(&record))
return wpabuf_alloc_copy(record.payload,
record.payload_length);
data += record.total_length;
len -= record.total_length;
}
wpa_printf(MSG_ERROR, "NDEF : Record not found");
return NULL;
}
static struct wpabuf * ndef_build_record(u8 flags, void *type,
u8 type_length, void *id,
u8 id_length, void *payload,
u32 payload_length)
{
struct wpabuf *record;
size_t total_len;
int short_record;
u8 local_flag;
short_record = payload_length < 256 ? 1 : 0;
total_len = 2; /* flag + type length */
/* payload length */
total_len += short_record ? sizeof(u8) : sizeof(u32);
if (id_length > 0)
total_len += 1;
total_len += type_length + id_length + payload_length;
record = wpabuf_alloc(total_len);
if (record == NULL) {
wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
"record for build");
return NULL;
}
local_flag = flags;
if (id_length > 0)
local_flag |= FLAG_ID_LENGTH_PRESENT;
if (short_record)
local_flag |= FLAG_SHORT_RECORD;
wpabuf_put_u8(record, local_flag);
wpabuf_put_u8(record, type_length);
if (short_record)
wpabuf_put_u8(record, payload_length);
else
wpabuf_put_be32(record, payload_length);
if (id_length > 0)
wpabuf_put_u8(record, id_length);
wpabuf_put_data(record, type, type_length);
wpabuf_put_data(record, id, id_length);
wpabuf_put_data(record, payload, payload_length);
return record;
}
static int wifi_filter(struct ndef_record *record)
{
if (record->type_length != os_strlen(wifi_handover_type))
return 0;
if (os_memcmp(record->type, wifi_handover_type,
os_strlen(wifi_handover_type)) != 0)
return 0;
return 1;
}
struct wpabuf * ndef_parse_wifi(struct wpabuf *buf)
{
return ndef_parse_records(buf, wifi_filter);
}
struct wpabuf * ndef_build_wifi(struct wpabuf *buf)
{
return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
FLAG_TNF_RFC2046, wifi_handover_type,
os_strlen(wifi_handover_type), NULL, 0,
wpabuf_mhead(buf), wpabuf_len(buf));
}

View file

@ -0,0 +1,252 @@
/*
* UPnP XML helper routines
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
#include "includes.h"
#include "common.h"
#include "base64.h"
#include "http.h"
#include "upnp_xml.h"
/*
* XML parsing and formatting
*
* XML is a markup language based on unicode; usually (and in our case,
* always!) based on utf-8. utf-8 uses a variable number of bytes per
* character. utf-8 has the advantage that all non-ASCII unicode characters are
* represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
* characters are single ascii bytes, thus we can use typical text processing.
*
* (One other interesting thing about utf-8 is that it is possible to look at
* any random byte and determine if it is the first byte of a character as
* versus a continuation byte).
*
* The base syntax of XML uses a few ASCII punctionation characters; any
* characters that would appear in the payload data are rewritten using
* sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
* Five such escapes total (more can be defined but that does not apply to our
* case). Thus we can safely parse for angle brackets etc.
*
* XML describes tree structures of tagged data, with each element beginning
* with an opening tag <label> and ending with a closing tag </label> with
* matching label. (There is also a self-closing tag <label/> which is supposed
* to be equivalent to <label></label>, i.e., no payload, but we are unlikely
* to see it for our purpose).
*
* Actually the opening tags are a little more complicated because they can
* contain "attributes" after the label (delimited by ascii space or tab chars)
* of the form attribute_label="value" or attribute_label='value'; as it turns
* out we do not have to read any of these attributes, just ignore them.
*
* Labels are any sequence of chars other than space, tab, right angle bracket
* (and ?), but may have an inner structure of <namespace><colon><plain_label>.
* As it turns out, we can ignore the namespaces, in fact we can ignore the
* entire tree hierarchy, because the plain labels we are looking for will be
* unique (not in general, but for this application). We do however have to be
* careful to skip over the namespaces.
*
* In generating XML we have to be more careful, but that is easy because
* everything we do is pretty canned. The only real care to take is to escape
* any special chars in our payload.
*/
/**
* xml_next_tag - Advance to next tag
* @in: Input
* @out: OUT: start of tag just after '<'
* @out_tagname: OUT: start of name of tag, skipping namespace
* @end: OUT: one after tag
* Returns: 0 on success, 1 on failure
*
* A tag has form:
* <left angle bracket><...><right angle bracket>
* Within the angle brackets, there is an optional leading forward slash (which
* makes the tag an ending tag), then an optional leading label (followed by
* colon) and then the tag name itself.
*
* Note that angle brackets present in the original data must have been encoded
* as &lt; and &gt; so they will not trouble us.
*/
static int xml_next_tag(const char *in, const char **out,
const char **out_tagname, const char **end)
{
while (*in && *in != '<')
in++;
if (*in != '<')
return 1;
*out = ++in;
if (*in == '/')
in++;
*out_tagname = in; /* maybe */
while (isalnum(*in) || *in == '-')
in++;
if (*in == ':')
*out_tagname = ++in;
while (*in && *in != '>')
in++;
if (*in != '>')
return 1;
*end = ++in;
return 0;
}
/* xml_data_encode -- format data for xml file, escaping special characters.
*
* Note that we assume we are using utf8 both as input and as output!
* In utf8, characters may be classed as follows:
* 0xxxxxxx(2) -- 1 byte ascii char
* 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
* 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
* 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
* 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
* 10xxxxxx(2) -- extension byte (6 payload bits per byte)
* Some values implied by the above are however illegal because they
* do not represent unicode chars or are not the shortest encoding.
* Actually, we can almost entirely ignore the above and just do
* text processing same as for ascii text.
*
* XML is written with arbitrary unicode characters, except that five
* characters have special meaning and so must be escaped where they
* appear in payload data... which we do here.
*/
void xml_data_encode(struct wpabuf *buf, const char *data, int len)
{
int i;
for (i = 0; i < len; i++) {
u8 c = ((u8 *) data)[i];
if (c == '<') {
wpabuf_put_str(buf, "&lt;");
continue;
}
if (c == '>') {
wpabuf_put_str(buf, "&gt;");
continue;
}
if (c == '&') {
wpabuf_put_str(buf, "&amp;");
continue;
}
if (c == '\'') {
wpabuf_put_str(buf, "&apos;");
continue;
}
if (c == '"') {
wpabuf_put_str(buf, "&quot;");
continue;
}
/*
* We could try to represent control characters using the
* sequence: &#x; where x is replaced by a hex numeral, but not
* clear why we would do this.
*/
wpabuf_put_u8(buf, c);
}
}
/* xml_add_tagged_data -- format tagged data as a new xml line.
*
* tag must not have any special chars.
* data may have special chars, which are escaped.
*/
void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data)
{
wpabuf_printf(buf, "<%s>", tag);
xml_data_encode(buf, data, os_strlen(data));
wpabuf_printf(buf, "</%s>\n", tag);
}
/* A POST body looks something like (per upnp spec):
* <?xml version="1.0"?>
* <s:Envelope
* xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
* s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
* <s:Body>
* <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
* <argumentName>in arg value</argumentName>
* other in args and their values go here, if any
* </u:actionName>
* </s:Body>
* </s:Envelope>
*
* where :
* s: might be some other namespace name followed by colon
* u: might be some other namespace name followed by colon
* actionName will be replaced according to action requested
* schema following actionName will be WFA scheme instead
* argumentName will be actual argument name
* (in arg value) will be actual argument value
*/
char * xml_get_first_item(const char *doc, const char *item)
{
const char *match = item;
int match_len = os_strlen(item);
const char *tag, *tagname, *end;
char *value;
/*
* This is crude: ignore any possible tag name conflicts and go right
* to the first tag of this name. This should be ok for the limited
* domain of UPnP messages.
*/
for (;;) {
if (xml_next_tag(doc, &tag, &tagname, &end))
return NULL;
doc = end;
if (!os_strncasecmp(tagname, match, match_len) &&
*tag != '/' &&
(tagname[match_len] == '>' ||
!isgraph(tagname[match_len]))) {
break;
}
}
end = doc;
while (*end && *end != '<')
end++;
value = os_zalloc(1 + (end - doc));
if (value == NULL)
return NULL;
os_memcpy(value, doc, end - doc);
return value;
}
struct wpabuf * xml_get_base64_item(const char *data, const char *name,
enum http_reply_code *ret)
{
char *msg;
struct wpabuf *buf;
unsigned char *decoded;
size_t len;
msg = xml_get_first_item(data, name);
if (msg == NULL) {
*ret = UPNP_ARG_VALUE_INVALID;
return NULL;
}
decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
os_free(msg);
if (decoded == NULL) {
*ret = UPNP_OUT_OF_MEMORY;
return NULL;
}
buf = wpabuf_alloc_ext_data(decoded, len);
if (buf == NULL) {
os_free(decoded);
*ret = UPNP_OUT_OF_MEMORY;
return NULL;
}
return buf;
}

View file

@ -0,0 +1,23 @@
/*
* UPnP XML helper routines
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
#ifndef UPNP_XML_H
#define UPNP_XML_H
#include "http.h"
void xml_data_encode(struct wpabuf *buf, const char *data, int len);
void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
const char *data);
char * xml_get_first_item(const char *doc, const char *item);
struct wpabuf * xml_get_base64_item(const char *data, const char *name,
enum http_reply_code *ret);
#endif /* UPNP_XML_H */

627
hostapd-0.8/src/wps/wps.c Normal file
View file

@ -0,0 +1,627 @@
/*
* Wi-Fi Protected Setup
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/dh_group5.h"
#include "common/ieee802_11_defs.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
#ifdef CONFIG_WPS_TESTING
int wps_version_number = 0x20;
int wps_testing_dummy_cred = 0;
#endif /* CONFIG_WPS_TESTING */
/**
* wps_init - Initialize WPS Registration protocol data
* @cfg: WPS configuration
* Returns: Pointer to allocated data or %NULL on failure
*
* This function is used to initialize WPS data for a registration protocol
* instance (i.e., each run of registration protocol as a Registrar of
* Enrollee. The caller is responsible for freeing this data after the
* registration run has been completed by calling wps_deinit().
*/
struct wps_data * wps_init(const struct wps_config *cfg)
{
struct wps_data *data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->wps = cfg->wps;
data->registrar = cfg->registrar;
if (cfg->registrar) {
os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN);
} else {
os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN);
os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
}
if (cfg->pin) {
data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
cfg->dev_pw_id : data->wps->oob_dev_pw_id;
data->dev_password = os_malloc(cfg->pin_len);
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
data->dev_password_len = cfg->pin_len;
}
data->pbc = cfg->pbc;
if (cfg->pbc) {
/* Use special PIN '00000000' for PBC */
data->dev_pw_id = DEV_PW_PUSHBUTTON;
os_free(data->dev_password);
data->dev_password = os_malloc(8);
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
os_memset(data->dev_password, '0', 8);
data->dev_password_len = 8;
}
data->state = data->registrar ? RECV_M1 : SEND_M1;
if (cfg->assoc_wps_ie) {
struct wps_parse_attr attr;
wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq",
cfg->assoc_wps_ie);
if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) {
wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE "
"from (Re)AssocReq");
} else if (attr.request_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute "
"in (Re)AssocReq WPS IE");
} else {
wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE "
"in (Re)AssocReq WPS IE): %d",
*attr.request_type);
data->request_type = *attr.request_type;
}
}
if (cfg->new_ap_settings) {
data->new_ap_settings =
os_malloc(sizeof(*data->new_ap_settings));
if (data->new_ap_settings == NULL) {
os_free(data);
return NULL;
}
os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
sizeof(*data->new_ap_settings));
}
if (cfg->peer_addr)
os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
if (cfg->p2p_dev_addr)
os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
data->use_psk_key = cfg->use_psk_key;
return data;
}
/**
* wps_deinit - Deinitialize WPS Registration protocol data
* @data: WPS Registration protocol data from wps_init()
*/
void wps_deinit(struct wps_data *data)
{
if (data->wps_pin_revealed) {
wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
"negotiation failed");
if (data->registrar)
wps_registrar_invalidate_pin(data->wps->registrar,
data->uuid_e);
} else if (data->registrar)
wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
wpabuf_free(data->dh_privkey);
wpabuf_free(data->dh_pubkey_e);
wpabuf_free(data->dh_pubkey_r);
wpabuf_free(data->last_msg);
os_free(data->dev_password);
os_free(data->new_psk);
wps_device_data_free(&data->peer_dev);
os_free(data->new_ap_settings);
dh5_free(data->dh_ctx);
os_free(data);
}
/**
* wps_process_msg - Process a WPS message
* @wps: WPS Registration protocol data from wps_init()
* @op_code: Message OP Code
* @msg: Message data
* Returns: Processing result
*
* This function is used to process WPS messages with OP Codes WSC_ACK,
* WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
* responsible for reassembling the messages before calling this function.
* Response to this message is built by calling wps_get_msg().
*/
enum wps_process_res wps_process_msg(struct wps_data *wps,
enum wsc_op_code op_code,
const struct wpabuf *msg)
{
if (wps->registrar)
return wps_registrar_process_msg(wps, op_code, msg);
else
return wps_enrollee_process_msg(wps, op_code, msg);
}
/**
* wps_get_msg - Build a WPS message
* @wps: WPS Registration protocol data from wps_init()
* @op_code: Buffer for returning message OP Code
* Returns: The generated WPS message or %NULL on failure
*
* This function is used to build a response to a message processed by calling
* wps_process_msg(). The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
{
if (wps->registrar)
return wps_registrar_get_msg(wps, op_code);
else
return wps_enrollee_get_msg(wps, op_code);
}
/**
* wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: 1 if PBC Registrar is active, 0 if not
*/
int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
{
struct wps_parse_attr attr;
/*
* In theory, this could also verify that attr.sel_reg_config_methods
* includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
* do not set Selected Registrar Config Methods attribute properly, so
* it is safer to just use Device Password ID here.
*/
if (wps_parse_msg(msg, &attr) < 0 ||
!attr.selected_registrar || *attr.selected_registrar == 0 ||
!attr.dev_password_id ||
WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
return 0;
#ifdef CONFIG_WPS_STRICT
if (!attr.sel_reg_config_methods ||
!(WPA_GET_BE16(attr.sel_reg_config_methods) &
WPS_CONFIG_PUSHBUTTON))
return 0;
#endif /* CONFIG_WPS_STRICT */
return 1;
}
static int is_selected_pin_registrar(struct wps_parse_attr *attr)
{
/*
* In theory, this could also verify that attr.sel_reg_config_methods
* includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
* but some deployed AP implementations do not set Selected Registrar
* Config Methods attribute properly, so it is safer to just use
* Device Password ID here.
*/
if (!attr->selected_registrar || *attr->selected_registrar == 0)
return 0;
if (attr->dev_password_id != NULL &&
WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
return 0;
#ifdef CONFIG_WPS_STRICT
if (!attr->sel_reg_config_methods ||
!(WPA_GET_BE16(attr->sel_reg_config_methods) &
(WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
return 0;
#endif /* CONFIG_WPS_STRICT */
return 1;
}
/**
* wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: 1 if PIN Registrar is active, 0 if not
*/
int wps_is_selected_pin_registrar(const struct wpabuf *msg)
{
struct wps_parse_attr attr;
if (wps_parse_msg(msg, &attr) < 0)
return 0;
return is_selected_pin_registrar(&attr);
}
/**
* wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
* @msg: WPS IE contents from Beacon or Probe Response frame
* @addr: MAC address to search for
* @ver1_compat: Whether to use version 1 compatibility mode
* Returns: 1 if address is authorized, 0 if not
*/
int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
int ver1_compat)
{
struct wps_parse_attr attr;
unsigned int i;
const u8 *pos;
const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
if (wps_parse_msg(msg, &attr) < 0)
return 0;
if (!attr.version2 && ver1_compat) {
/*
* Version 1.0 AP - AuthorizedMACs not used, so revert back to
* old mechanism of using SelectedRegistrar.
*/
return is_selected_pin_registrar(&attr);
}
if (!attr.authorized_macs)
return 0;
pos = attr.authorized_macs;
for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
if (os_memcmp(pos, addr, ETH_ALEN) == 0 ||
os_memcmp(pos, bcast, ETH_ALEN) == 0)
return 1;
pos += ETH_ALEN;
}
return 0;
}
/**
* wps_ap_priority_compar - Prioritize WPS IE from two APs
* @wps_a: WPS IE contents from Beacon or Probe Response frame
* @wps_b: WPS IE contents from Beacon or Probe Response frame
* Returns: 1 if wps_b is considered more likely selection for WPS
* provisioning, -1 if wps_a is considered more like, or 0 if no preference
*/
int wps_ap_priority_compar(const struct wpabuf *wps_a,
const struct wpabuf *wps_b)
{
struct wps_parse_attr attr_a, attr_b;
int sel_a, sel_b;
if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
return 1;
if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
return -1;
sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0;
if (sel_a && !sel_b)
return -1;
if (!sel_a && sel_b)
return 1;
return 0;
}
/**
* wps_get_uuid_e - Get UUID-E from WPS IE
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: Pointer to UUID-E or %NULL if not included
*
* The returned pointer is to the msg contents and it remains valid only as
* long as the msg buffer is valid.
*/
const u8 * wps_get_uuid_e(const struct wpabuf *msg)
{
struct wps_parse_attr attr;
if (wps_parse_msg(msg, &attr) < 0)
return NULL;
return attr.uuid_e;
}
/**
* wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
* @req_type: Value for Request Type attribute
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
{
struct wpabuf *ie;
u8 *len;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
"Request");
ie = wpabuf_alloc(100);
if (ie == NULL)
return NULL;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(ie, 1);
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
wps_build_wfa_ext(ie, 0, NULL, 0)) {
wpabuf_free(ie);
return NULL;
}
*len = wpabuf_len(ie) - 2;
return ie;
}
/**
* wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_build_assoc_resp_ie(void)
{
struct wpabuf *ie;
u8 *len;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
"Response");
ie = wpabuf_alloc(100);
if (ie == NULL)
return NULL;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(ie, 1);
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
wps_build_resp_type(ie, WPS_RESP_AP) ||
wps_build_wfa_ext(ie, 0, NULL, 0)) {
wpabuf_free(ie);
return NULL;
}
*len = wpabuf_len(ie) - 2;
return ie;
}
/**
* wps_build_probe_req_ie - Build WPS IE for Probe Request
* @pbc: Whether searching for PBC mode APs
* @dev: Device attributes
* @uuid: Own UUID
* @req_type: Value for Request Type attribute
* @num_req_dev_types: Number of requested device types
* @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
* %NULL if none
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
const u8 *uuid,
enum wps_request_type req_type,
unsigned int num_req_dev_types,
const u8 *req_dev_types)
{
struct wpabuf *ie;
u16 methods = 0;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
ie = wpabuf_alloc(500);
if (ie == NULL)
return NULL;
methods |= WPS_CONFIG_PUSHBUTTON;
#ifdef CONFIG_WPS2
/*
* TODO: Should figure out whether this device has a physical or
* virtual pushbutton.
*/
methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
#endif /* CONFIG_WPS2 */
/*
* TODO: Should figure out whether this Probe Request was triggered
* using physical or virtual display. Also, if the device has a PIN on
* a label, that should be indicated here.
*/
methods |= WPS_CONFIG_DISPLAY |
#ifdef CONFIG_WPS2
WPS_CONFIG_VIRT_DISPLAY |
#endif /* CONFIG_WPS2 */
WPS_CONFIG_KEYPAD;
#ifdef CONFIG_WPS_UFD
methods |= WPS_CONFIG_USBA;
#endif /* CONFIG_WPS_UFD */
#ifdef CONFIG_WPS_NFC
methods |= WPS_CONFIG_NFC_INTERFACE;
#endif /* CONFIG_WPS_NFC */
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
wps_build_config_methods(ie, methods) ||
wps_build_uuid_e(ie, uuid) ||
wps_build_primary_dev_type(dev, ie) ||
wps_build_rf_bands(dev, ie) ||
wps_build_assoc_state(NULL, ie) ||
wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
DEV_PW_DEFAULT) ||
#ifdef CONFIG_WPS2
wps_build_manufacturer(dev, ie) ||
wps_build_model_name(dev, ie) ||
wps_build_model_number(dev, ie) ||
wps_build_dev_name(dev, ie) ||
wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
#endif /* CONFIG_WPS2 */
wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
||
wps_build_secondary_dev_type(dev, ie)
) {
wpabuf_free(ie);
return NULL;
}
#ifndef CONFIG_WPS2
if (dev->p2p && wps_build_dev_name(dev, ie)) {
wpabuf_free(ie);
return NULL;
}
#endif /* CONFIG_WPS2 */
return wps_ie_encapsulate(ie);
}
void wps_free_pending_msgs(struct upnp_pending_message *msgs)
{
struct upnp_pending_message *p, *prev;
p = msgs;
while (p) {
prev = p;
p = p->next;
wpabuf_free(prev->msg);
os_free(prev);
}
}
int wps_attr_text(struct wpabuf *data, char *buf, char *end)
{
struct wps_parse_attr attr;
char *pos = buf;
int ret;
if (wps_parse_msg(data, &attr) < 0)
return -1;
if (attr.wps_state) {
if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED)
ret = os_snprintf(pos, end - pos,
"wps_state=unconfigured\n");
else if (*attr.wps_state == WPS_STATE_CONFIGURED)
ret = os_snprintf(pos, end - pos,
"wps_state=configured\n");
else
ret = 0;
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (attr.ap_setup_locked && *attr.ap_setup_locked) {
ret = os_snprintf(pos, end - pos,
"wps_ap_setup_locked=1\n");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (attr.selected_registrar && *attr.selected_registrar) {
ret = os_snprintf(pos, end - pos,
"wps_selected_registrar=1\n");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (attr.dev_password_id) {
ret = os_snprintf(pos, end - pos,
"wps_device_password_id=%u\n",
WPA_GET_BE16(attr.dev_password_id));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (attr.sel_reg_config_methods) {
ret = os_snprintf(pos, end - pos,
"wps_selected_registrar_config_methods="
"0x%04x\n",
WPA_GET_BE16(attr.sel_reg_config_methods));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (attr.primary_dev_type) {
char devtype[WPS_DEV_TYPE_BUFSIZE];
ret = os_snprintf(pos, end - pos,
"wps_primary_device_type=%s\n",
wps_dev_type_bin2str(attr.primary_dev_type,
devtype,
sizeof(devtype)));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (attr.dev_name) {
char *str = os_malloc(attr.dev_name_len + 1);
size_t i;
if (str == NULL)
return pos - buf;
for (i = 0; i < attr.dev_name_len; i++) {
if (attr.dev_name[i] < 32)
str[i] = '_';
else
str[i] = attr.dev_name[i];
}
str[i] = '\0';
ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
os_free(str);
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
if (attr.config_methods) {
ret = os_snprintf(pos, end - pos,
"wps_config_methods=0x%04x\n",
WPA_GET_BE16(attr.config_methods));
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
}
return pos - buf;
}

964
hostapd-0.8/src/wps/wps.h Normal file
View file

@ -0,0 +1,964 @@
/*
* Wi-Fi Protected Setup
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef WPS_H
#define WPS_H
#include "wps_defs.h"
/**
* enum wsc_op_code - EAP-WSC OP-Code values
*/
enum wsc_op_code {
WSC_UPnP = 0 /* No OP Code in UPnP transport */,
WSC_Start = 0x01,
WSC_ACK = 0x02,
WSC_NACK = 0x03,
WSC_MSG = 0x04,
WSC_Done = 0x05,
WSC_FRAG_ACK = 0x06
};
struct wps_registrar;
struct upnp_wps_device_sm;
struct wps_er;
/**
* struct wps_credential - WPS Credential
* @ssid: SSID
* @ssid_len: Length of SSID
* @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags)
* @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags)
* @key_idx: Key index
* @key: Key
* @key_len: Key length in octets
* @mac_addr: MAC address of the Credential receiver
* @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
* this may be %NULL, if not used
* @cred_attr_len: Length of cred_attr in octets
*/
struct wps_credential {
u8 ssid[32];
size_t ssid_len;
u16 auth_type;
u16 encr_type;
u8 key_idx;
u8 key[64];
size_t key_len;
u8 mac_addr[ETH_ALEN];
const u8 *cred_attr;
size_t cred_attr_len;
};
#define WPS_DEV_TYPE_LEN 8
#define WPS_DEV_TYPE_BUFSIZE 21
#define WPS_SEC_DEV_TYPE_MAX_LEN 128
/* maximum number of advertised WPS vendor extension attributes */
#define MAX_WPS_VENDOR_EXTENSIONS 10
/* maximum size of WPS Vendor extension attribute */
#define WPS_MAX_VENDOR_EXT_LEN 1024
/* maximum number of parsed WPS vendor extension attributes */
#define MAX_WPS_PARSE_VENDOR_EXT 10
/**
* struct wps_device_data - WPS Device Data
* @mac_addr: Device MAC address
* @device_name: Device Name (0..32 octets encoded in UTF-8)
* @manufacturer: Manufacturer (0..64 octets encoded in UTF-8)
* @model_name: Model Name (0..32 octets encoded in UTF-8)
* @model_number: Model Number (0..32 octets encoded in UTF-8)
* @serial_number: Serial Number (0..32 octets encoded in UTF-8)
* @pri_dev_type: Primary Device Type
* @sec_dev_type: Array of secondary device types
* @num_sec_dev_type: Number of secondary device types
* @os_version: OS Version
* @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
* @p2p: Whether the device is a P2P device
*/
struct wps_device_data {
u8 mac_addr[ETH_ALEN];
char *device_name;
char *manufacturer;
char *model_name;
char *model_number;
char *serial_number;
u8 pri_dev_type[WPS_DEV_TYPE_LEN];
#define WPS_SEC_DEVICE_TYPES 5
u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
u8 num_sec_dev_types;
u32 os_version;
u8 rf_bands;
struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
int p2p;
};
struct oob_conf_data {
enum {
OOB_METHOD_UNKNOWN = 0,
OOB_METHOD_DEV_PWD_E,
OOB_METHOD_DEV_PWD_R,
OOB_METHOD_CRED,
} oob_method;
struct wpabuf *dev_password;
struct wpabuf *pubkey_hash;
};
/**
* struct wps_config - WPS configuration for a single registration protocol run
*/
struct wps_config {
/**
* wps - Pointer to long term WPS context
*/
struct wps_context *wps;
/**
* registrar - Whether this end is a Registrar
*/
int registrar;
/**
* pin - Enrollee Device Password (%NULL for Registrar or PBC)
*/
const u8 *pin;
/**
* pin_len - Length on pin in octets
*/
size_t pin_len;
/**
* pbc - Whether this is protocol run uses PBC
*/
int pbc;
/**
* assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
*/
const struct wpabuf *assoc_wps_ie;
/**
* new_ap_settings - New AP settings (%NULL if not used)
*
* This parameter provides new AP settings when using a wireless
* stations as a Registrar to configure the AP. %NULL means that AP
* will not be reconfigured, i.e., the station will only learn the
* current AP settings by using AP PIN.
*/
const struct wps_credential *new_ap_settings;
/**
* peer_addr: MAC address of the peer in AP; %NULL if not AP
*/
const u8 *peer_addr;
/**
* use_psk_key - Use PSK format key in Credential
*
* Force PSK format to be used instead of ASCII passphrase when
* building Credential for an Enrollee. The PSK value is set in
* struct wpa_context::psk.
*/
int use_psk_key;
/**
* dev_pw_id - Device Password ID for Enrollee when PIN is used
*/
u16 dev_pw_id;
/**
* p2p_dev_addr - P2P Device Address from (Re)Association Request
*
* On AP/GO, this is set to the P2P Device Address of the associating
* P2P client if a P2P IE is included in the (Re)Association Request
* frame and the P2P Device Address is included. Otherwise, this is set
* to %NULL to indicate the station does not have a P2P Device Address.
*/
const u8 *p2p_dev_addr;
};
struct wps_data * wps_init(const struct wps_config *cfg);
void wps_deinit(struct wps_data *data);
/**
* enum wps_process_res - WPS message processing result
*/
enum wps_process_res {
/**
* WPS_DONE - Processing done
*/
WPS_DONE,
/**
* WPS_CONTINUE - Processing continues
*/
WPS_CONTINUE,
/**
* WPS_FAILURE - Processing failed
*/
WPS_FAILURE,
/**
* WPS_PENDING - Processing continues, but waiting for an external
* event (e.g., UPnP message from an external Registrar)
*/
WPS_PENDING
};
enum wps_process_res wps_process_msg(struct wps_data *wps,
enum wsc_op_code op_code,
const struct wpabuf *msg);
struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
int wps_is_selected_pbc_registrar(const struct wpabuf *msg);
int wps_is_selected_pin_registrar(const struct wpabuf *msg);
int wps_ap_priority_compar(const struct wpabuf *wps_a,
const struct wpabuf *wps_b);
int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
int ver1_compat);
const u8 * wps_get_uuid_e(const struct wpabuf *msg);
struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
struct wpabuf * wps_build_assoc_resp_ie(void);
struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
const u8 *uuid,
enum wps_request_type req_type,
unsigned int num_req_dev_types,
const u8 *req_dev_types);
/**
* struct wps_registrar_config - WPS Registrar configuration
*/
struct wps_registrar_config {
/**
* new_psk_cb - Callback for new PSK
* @ctx: Higher layer context data (cb_ctx)
* @mac_addr: MAC address of the Enrollee
* @psk: The new PSK
* @psk_len: The length of psk in octets
* Returns: 0 on success, -1 on failure
*
* This callback is called when a new per-device PSK is provisioned.
*/
int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
size_t psk_len);
/**
* set_ie_cb - Callback for WPS IE changes
* @ctx: Higher layer context data (cb_ctx)
* @beacon_ie: WPS IE for Beacon
* @probe_resp_ie: WPS IE for Probe Response
* Returns: 0 on success, -1 on failure
*
* This callback is called whenever the WPS IE in Beacon or Probe
* Response frames needs to be changed (AP only). Callee is responsible
* for freeing the buffers.
*/
int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
struct wpabuf *probe_resp_ie);
/**
* pin_needed_cb - Callback for requesting a PIN
* @ctx: Higher layer context data (cb_ctx)
* @uuid_e: UUID-E of the unknown Enrollee
* @dev: Device Data from the unknown Enrollee
*
* This callback is called whenever an unknown Enrollee requests to use
* PIN method and a matching PIN (Device Password) is not found in
* Registrar data.
*/
void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
const struct wps_device_data *dev);
/**
* reg_success_cb - Callback for reporting successful registration
* @ctx: Higher layer context data (cb_ctx)
* @mac_addr: MAC address of the Enrollee
* @uuid_e: UUID-E of the Enrollee
*
* This callback is called whenever an Enrollee completes registration
* successfully.
*/
void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e);
/**
* set_sel_reg_cb - Callback for reporting selected registrar changes
* @ctx: Higher layer context data (cb_ctx)
* @sel_reg: Whether the Registrar is selected
* @dev_passwd_id: Device Password ID to indicate with method or
* specific password the Registrar intends to use
* @sel_reg_config_methods: Bit field of active config methods
*
* This callback is called whenever the Selected Registrar state
* changes (e.g., a new PIN becomes available or PBC is invoked). This
* callback is only used by External Registrar implementation;
* set_ie_cb() is used by AP implementation in similar caes, but it
* provides the full WPS IE data instead of just the minimal Registrar
* state information.
*/
void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods);
/**
* enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq
* @ctx: Higher layer context data (cb_ctx)
* @addr: MAC address of the Enrollee
* @uuid_e: UUID of the Enrollee
* @pri_dev_type: Primary device type
* @config_methods: Config Methods
* @dev_password_id: Device Password ID
* @request_type: Request Type
* @dev_name: Device Name (if available)
*/
void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
const u8 *pri_dev_type, u16 config_methods,
u16 dev_password_id, u8 request_type,
const char *dev_name);
/**
* cb_ctx: Higher layer context data for Registrar callbacks
*/
void *cb_ctx;
/**
* skip_cred_build: Do not build credential
*
* This option can be used to disable internal code that builds
* Credential attribute into M8 based on the current network
* configuration and Enrollee capabilities. The extra_cred data will
* then be used as the Credential(s).
*/
int skip_cred_build;
/**
* extra_cred: Additional Credential attribute(s)
*
* This optional data (set to %NULL to disable) can be used to add
* Credential attribute(s) for other networks into M8. If
* skip_cred_build is set, this will also override the automatically
* generated Credential attribute.
*/
const u8 *extra_cred;
/**
* extra_cred_len: Length of extra_cred in octets
*/
size_t extra_cred_len;
/**
* disable_auto_conf - Disable auto-configuration on first registration
*
* By default, the AP that is started in not configured state will
* generate a random PSK and move to configured state when the first
* registration protocol run is completed successfully. This option can
* be used to disable this functionality and leave it up to an external
* program to take care of configuration. This requires the extra_cred
* to be set with a suitable Credential and skip_cred_build being used.
*/
int disable_auto_conf;
/**
* static_wep_only - Whether the BSS supports only static WEP
*/
int static_wep_only;
/**
* dualband - Whether this is a concurrent dualband AP
*/
int dualband;
};
/**
* enum wps_event - WPS event types
*/
enum wps_event {
/**
* WPS_EV_M2D - M2D received (Registrar did not know us)
*/
WPS_EV_M2D,
/**
* WPS_EV_FAIL - Registration failed
*/
WPS_EV_FAIL,
/**
* WPS_EV_SUCCESS - Registration succeeded
*/
WPS_EV_SUCCESS,
/**
* WPS_EV_PWD_AUTH_FAIL - Password authentication failed
*/
WPS_EV_PWD_AUTH_FAIL,
/**
* WPS_EV_PBC_OVERLAP - PBC session overlap detected
*/
WPS_EV_PBC_OVERLAP,
/**
* WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start
*/
WPS_EV_PBC_TIMEOUT,
/**
* WPS_EV_ER_AP_ADD - ER: AP added
*/
WPS_EV_ER_AP_ADD,
/**
* WPS_EV_ER_AP_REMOVE - ER: AP removed
*/
WPS_EV_ER_AP_REMOVE,
/**
* WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added
*/
WPS_EV_ER_ENROLLEE_ADD,
/**
* WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
*/
WPS_EV_ER_ENROLLEE_REMOVE,
/**
* WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
*/
WPS_EV_ER_AP_SETTINGS,
/**
* WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
*/
WPS_EV_ER_SET_SELECTED_REGISTRAR
};
/**
* union wps_event_data - WPS event data
*/
union wps_event_data {
/**
* struct wps_event_m2d - M2D event data
*/
struct wps_event_m2d {
u16 config_methods;
const u8 *manufacturer;
size_t manufacturer_len;
const u8 *model_name;
size_t model_name_len;
const u8 *model_number;
size_t model_number_len;
const u8 *serial_number;
size_t serial_number_len;
const u8 *dev_name;
size_t dev_name_len;
const u8 *primary_dev_type; /* 8 octets */
u16 config_error;
u16 dev_password_id;
} m2d;
/**
* struct wps_event_fail - Registration failure information
* @msg: enum wps_msg_type
*/
struct wps_event_fail {
int msg;
u16 config_error;
u16 error_indication;
} fail;
struct wps_event_pwd_auth_fail {
int enrollee;
int part;
} pwd_auth_fail;
struct wps_event_er_ap {
const u8 *uuid;
const u8 *mac_addr;
const char *friendly_name;
const char *manufacturer;
const char *manufacturer_url;
const char *model_description;
const char *model_name;
const char *model_number;
const char *model_url;
const char *serial_number;
const char *upc;
const u8 *pri_dev_type;
u8 wps_state;
} ap;
struct wps_event_er_enrollee {
const u8 *uuid;
const u8 *mac_addr;
int m1_received;
u16 config_methods;
u16 dev_passwd_id;
const u8 *pri_dev_type;
const char *dev_name;
const char *manufacturer;
const char *model_name;
const char *model_number;
const char *serial_number;
} enrollee;
struct wps_event_er_ap_settings {
const u8 *uuid;
const struct wps_credential *cred;
} ap_settings;
struct wps_event_er_set_selected_registrar {
const u8 *uuid;
int sel_reg;
u16 dev_passwd_id;
u16 sel_reg_config_methods;
enum {
WPS_ER_SET_SEL_REG_START,
WPS_ER_SET_SEL_REG_DONE,
WPS_ER_SET_SEL_REG_FAILED
} state;
} set_sel_reg;
};
/**
* struct upnp_pending_message - Pending PutWLANResponse messages
* @next: Pointer to next pending message or %NULL
* @addr: NewWLANEventMAC
* @msg: NewMessage
* @type: Message Type
*/
struct upnp_pending_message {
struct upnp_pending_message *next;
u8 addr[ETH_ALEN];
struct wpabuf *msg;
enum wps_msg_type type;
};
/**
* struct wps_context - Long term WPS context data
*
* This data is stored at the higher layer Authenticator or Supplicant data
* structures and it is maintained over multiple registration protocol runs.
*/
struct wps_context {
/**
* ap - Whether the local end is an access point
*/
int ap;
/**
* registrar - Pointer to WPS registrar data from wps_registrar_init()
*/
struct wps_registrar *registrar;
/**
* wps_state - Current WPS state
*/
enum wps_state wps_state;
/**
* ap_setup_locked - Whether AP setup is locked (only used at AP)
*/
int ap_setup_locked;
/**
* uuid - Own UUID
*/
u8 uuid[16];
/**
* ssid - SSID
*
* This SSID is used by the Registrar to fill in information for
* Credentials. In addition, AP uses it when acting as an Enrollee to
* notify Registrar of the current configuration.
*/
u8 ssid[32];
/**
* ssid_len - Length of ssid in octets
*/
size_t ssid_len;
/**
* dev - Own WPS device data
*/
struct wps_device_data dev;
/**
* oob_conf - OOB Config data
*/
struct oob_conf_data oob_conf;
/**
* oob_dev_pw_id - OOB Device password id
*/
u16 oob_dev_pw_id;
/**
* dh_ctx - Context data for Diffie-Hellman operation
*/
void *dh_ctx;
/**
* dh_privkey - Diffie-Hellman private key
*/
struct wpabuf *dh_privkey;
/**
* dh_pubkey_oob - Diffie-Hellman public key
*/
struct wpabuf *dh_pubkey;
/**
* config_methods - Enabled configuration methods
*
* Bit field of WPS_CONFIG_*
*/
u16 config_methods;
/**
* encr_types - Enabled encryption types (bit field of WPS_ENCR_*)
*/
u16 encr_types;
/**
* auth_types - Authentication types (bit field of WPS_AUTH_*)
*/
u16 auth_types;
/**
* network_key - The current Network Key (PSK) or %NULL to generate new
*
* If %NULL, Registrar will generate per-device PSK. In addition, AP
* uses this when acting as an Enrollee to notify Registrar of the
* current configuration.
*
* When using WPA/WPA2-Person, this key can be either the ASCII
* passphrase (8..63 characters) or the 32-octet PSK (64 hex
* characters). When this is set to the ASCII passphrase, the PSK can
* be provided in the psk buffer and used per-Enrollee to control which
* key type is included in the Credential (e.g., to reduce calculation
* need on low-powered devices by provisioning PSK while still allowing
* other devices to get the passphrase).
*/
u8 *network_key;
/**
* network_key_len - Length of network_key in octets
*/
size_t network_key_len;
/**
* psk - The current network PSK
*
* This optional value can be used to provide the current PSK if
* network_key is set to the ASCII passphrase.
*/
u8 psk[32];
/**
* psk_set - Whether psk value is set
*/
int psk_set;
/**
* ap_settings - AP Settings override for M7 (only used at AP)
*
* If %NULL, AP Settings attributes will be generated based on the
* current network configuration.
*/
u8 *ap_settings;
/**
* ap_settings_len - Length of ap_settings in octets
*/
size_t ap_settings_len;
/**
* friendly_name - Friendly Name (required for UPnP)
*/
char *friendly_name;
/**
* manufacturer_url - Manufacturer URL (optional for UPnP)
*/
char *manufacturer_url;
/**
* model_description - Model Description (recommended for UPnP)
*/
char *model_description;
/**
* model_url - Model URL (optional for UPnP)
*/
char *model_url;
/**
* upc - Universal Product Code (optional for UPnP)
*/
char *upc;
/**
* cred_cb - Callback to notify that new Credentials were received
* @ctx: Higher layer context data (cb_ctx)
* @cred: The received Credential
* Return: 0 on success, -1 on failure
*/
int (*cred_cb)(void *ctx, const struct wps_credential *cred);
/**
* event_cb - Event callback (state information about progress)
* @ctx: Higher layer context data (cb_ctx)
* @event: Event type
* @data: Event data
*/
void (*event_cb)(void *ctx, enum wps_event event,
union wps_event_data *data);
/**
* cb_ctx: Higher layer context data for callbacks
*/
void *cb_ctx;
struct upnp_wps_device_sm *wps_upnp;
/* Pending messages from UPnP PutWLANResponse */
struct upnp_pending_message *upnp_msgs;
};
struct oob_device_data {
char *device_name;
char *device_path;
void * (*init_func)(struct wps_context *, struct oob_device_data *,
int);
struct wpabuf * (*read_func)(void *);
int (*write_func)(void *, struct wpabuf *);
void (*deinit_func)(void *);
};
struct oob_nfc_device_data {
int (*init_func)(char *);
void * (*read_func)(size_t *);
int (*write_func)(void *, size_t);
void (*deinit_func)(void);
};
struct wps_registrar *
wps_registrar_init(struct wps_context *wps,
const struct wps_registrar_config *cfg);
void wps_registrar_deinit(struct wps_registrar *reg);
int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
const u8 *uuid, const u8 *pin, size_t pin_len,
int timeout);
int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
int wps_registrar_wps_cancel(struct wps_registrar *reg);
int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
int wps_registrar_button_pushed(struct wps_registrar *reg,
const u8 *p2p_dev_addr);
void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
const struct wpabuf *wps_data,
int p2p_wildcard);
int wps_registrar_update_ie(struct wps_registrar *reg);
int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
char *buf, size_t buflen);
int wps_registrar_config_ap(struct wps_registrar *reg,
struct wps_credential *cred);
unsigned int wps_pin_checksum(unsigned int pin);
unsigned int wps_pin_valid(unsigned int pin);
unsigned int wps_generate_pin(void);
void wps_free_pending_msgs(struct upnp_pending_message *msgs);
struct oob_device_data * wps_get_oob_device(char *device_type);
struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name);
int wps_get_oob_method(char *method);
int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
int registrar);
int wps_attr_text(struct wpabuf *data, char *buf, char *end);
struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
const char *filter);
void wps_er_refresh(struct wps_er *er);
void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods);
int wps_er_pbc(struct wps_er *er, const u8 *uuid);
int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
size_t pin_len);
int wps_er_set_config(struct wps_er *er, const u8 *uuid,
const struct wps_credential *cred);
int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
size_t pin_len, const struct wps_credential *cred);
int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
size_t buf_len);
void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
u16 wps_config_methods_str2bin(const char *str);
#ifdef CONFIG_WPS_STRICT
int wps_validate_beacon(const struct wpabuf *wps_ie);
int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
const u8 *addr);
int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
int wps_validate_assoc_req(const struct wpabuf *wps_ie);
int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
int wps_validate_m1(const struct wpabuf *tlvs);
int wps_validate_m2(const struct wpabuf *tlvs);
int wps_validate_m2d(const struct wpabuf *tlvs);
int wps_validate_m3(const struct wpabuf *tlvs);
int wps_validate_m4(const struct wpabuf *tlvs);
int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
int wps_validate_m5(const struct wpabuf *tlvs);
int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
int wps_validate_m6(const struct wpabuf *tlvs);
int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
int wps_validate_m7(const struct wpabuf *tlvs);
int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
int wps_validate_m8(const struct wpabuf *tlvs);
int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
int wps_validate_wsc_ack(const struct wpabuf *tlvs);
int wps_validate_wsc_nack(const struct wpabuf *tlvs);
int wps_validate_wsc_done(const struct wpabuf *tlvs);
int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
#else /* CONFIG_WPS_STRICT */
static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
return 0;
}
static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
int probe, const u8 *addr)
{
return 0;
}
static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
const u8 *addr)
{
return 0;
}
static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
{
return 0;
}
static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
{
return 0;
}
static inline int wps_validate_m1(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m2(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m2d(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m3(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m4(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
{
return 0;
}
static inline int wps_validate_m5(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
{
return 0;
}
static inline int wps_validate_m6(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
{
return 0;
}
static inline int wps_validate_m7(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
int wps2)
{
return 0;
}
static inline int wps_validate_m8(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
int wps2)
{
return 0;
}
static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
{
return 0;
}
static inline int wps_validate_upnp_set_selected_registrar(
const struct wpabuf *tlvs)
{
return 0;
}
#endif /* CONFIG_WPS_STRICT */
#endif /* WPS_H */

View file

@ -0,0 +1,422 @@
/*
* Wi-Fi Protected Setup - attribute building
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/dh_group5.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "wps_i.h"
int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
{
struct wpabuf *pubkey;
wpa_printf(MSG_DEBUG, "WPS: * Public Key");
wpabuf_free(wps->dh_privkey);
if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) {
wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
wps->dh_ctx = wps->wps->dh_ctx;
wps->wps->dh_ctx = NULL;
pubkey = wpabuf_dup(wps->wps->dh_pubkey);
} else {
wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
wps->dh_privkey = NULL;
dh5_free(wps->dh_ctx);
wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
pubkey = wpabuf_zeropad(pubkey, 192);
}
if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
"Diffie-Hellman handshake");
wpabuf_free(pubkey);
return -1;
}
wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
wpabuf_put_be16(msg, wpabuf_len(pubkey));
wpabuf_put_buf(msg, pubkey);
if (wps->registrar) {
wpabuf_free(wps->dh_pubkey_r);
wps->dh_pubkey_r = pubkey;
} else {
wpabuf_free(wps->dh_pubkey_e);
wps->dh_pubkey_e = pubkey;
}
return 0;
}
int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
{
wpa_printf(MSG_DEBUG, "WPS: * Request Type");
wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, type);
return 0;
}
int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
{
wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", type);
wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, type);
return 0;
}
int wps_build_config_methods(struct wpabuf *msg, u16 methods)
{
wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, methods);
return 0;
}
int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
{
wpa_printf(MSG_DEBUG, "WPS: * UUID-E");
wpabuf_put_be16(msg, ATTR_UUID_E);
wpabuf_put_be16(msg, WPS_UUID_LEN);
wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
return 0;
}
int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
{
wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, id);
return 0;
}
int wps_build_config_error(struct wpabuf *msg, u16 err)
{
wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err);
wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, err);
return 0;
}
int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
{
u8 hash[SHA256_MAC_LEN];
const u8 *addr[2];
size_t len[2];
if (wps->last_msg == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
"building authenticator");
return -1;
}
/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
* (M_curr* is M_curr without the Authenticator attribute)
*/
addr[0] = wpabuf_head(wps->last_msg);
len[0] = wpabuf_len(wps->last_msg);
addr[1] = wpabuf_head(msg);
len[1] = wpabuf_len(msg);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
wpa_printf(MSG_DEBUG, "WPS: * Authenticator");
wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
return 0;
}
int wps_build_version(struct wpabuf *msg)
{
/*
* Note: This attribute is deprecated and set to hardcoded 0x10 for
* backwards compatibility reasons. The real version negotiation is
* done with Version2.
*/
wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)");
wpabuf_put_be16(msg, ATTR_VERSION);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 0x10);
return 0;
}
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
const u8 *auth_macs, size_t auth_macs_count)
{
#ifdef CONFIG_WPS2
u8 *len;
wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
len = wpabuf_put(msg, 2); /* to be filled */
wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION);
wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
wpabuf_put_u8(msg, 1);
wpabuf_put_u8(msg, WPS_VERSION);
if (req_to_enroll) {
wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)");
wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
wpabuf_put_u8(msg, 1);
wpabuf_put_u8(msg, 1);
}
if (auth_macs && auth_macs_count) {
size_t i;
wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)",
(int) auth_macs_count);
wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
for (i = 0; i < auth_macs_count; i++)
wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR,
MAC2STR(&auth_macs[i * ETH_ALEN]));
}
WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
#endif /* CONFIG_WPS2 */
#ifdef CONFIG_WPS_TESTING
if (WPS_VERSION > 0x20) {
wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra "
"attribute");
wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 42);
}
#endif /* CONFIG_WPS_TESTING */
return 0;
}
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
{
wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type);
wpabuf_put_be16(msg, ATTR_MSG_TYPE);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, msg_type);
return 0;
}
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce");
wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
return 0;
}
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce");
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
return 0;
}
int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
u16 auth_types = WPS_AUTH_TYPES;
#ifdef CONFIG_WPS2
auth_types &= ~WPS_AUTH_SHARED;
#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, auth_types);
return 0;
}
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
u16 encr_types = WPS_ENCR_TYPES;
#ifdef CONFIG_WPS2
encr_types &= ~WPS_ENCR_WEP;
#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, encr_types);
return 0;
}
int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags");
wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, WPS_CONN_ESS);
return 0;
}
int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * Association State");
wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
return 0;
}
int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
{
u8 hash[SHA256_MAC_LEN];
wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator");
hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
wpabuf_len(msg), hash);
wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
wpabuf_put_be16(msg, WPS_KWA_LEN);
wpabuf_put_data(msg, hash, WPS_KWA_LEN);
return 0;
}
int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
struct wpabuf *plain)
{
size_t pad_len;
const size_t block_size = 16;
u8 *iv, *data;
wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings");
/* PKCS#5 v2.0 pad */
pad_len = block_size - wpabuf_len(plain) % block_size;
os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
iv = wpabuf_put(msg, block_size);
if (random_get_bytes(iv, block_size) < 0)
return -1;
data = wpabuf_put(msg, 0);
wpabuf_put_buf(msg, plain);
if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
return -1;
return 0;
}
#ifdef CONFIG_WPS_OOB
int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
{
size_t hash_len;
const u8 *addr[1];
u8 pubkey_hash[WPS_HASH_LEN];
u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password");
addr[0] = wpabuf_head(wps->dh_pubkey);
hash_len = wpabuf_len(wps->dh_pubkey);
sha256_vector(1, addr, &hash_len, pubkey_hash);
if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
wpa_printf(MSG_ERROR, "WPS: device password id "
"generation error");
return -1;
}
wps->oob_dev_pw_id |= 0x0010;
if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) <
0) {
wpa_printf(MSG_ERROR, "WPS: OOB device password "
"generation error");
return -1;
}
wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
wpabuf_put_be16(msg, wps->oob_dev_pw_id);
wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
wpa_snprintf_hex_uppercase(
wpabuf_put(wps->oob_conf.dev_password,
wpabuf_size(wps->oob_conf.dev_password)),
wpabuf_size(wps->oob_conf.dev_password),
dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
return 0;
}
#endif /* CONFIG_WPS_OOB */
/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
{
struct wpabuf *ie;
const u8 *pos, *end;
ie = wpabuf_alloc(wpabuf_len(data) + 100);
if (ie == NULL) {
wpabuf_free(data);
return NULL;
}
pos = wpabuf_head(data);
end = pos + wpabuf_len(data);
while (end > pos) {
size_t frag_len = end - pos;
if (frag_len > 251)
frag_len = 251;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
wpabuf_put_u8(ie, 4 + frag_len);
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
wpabuf_put_data(ie, pos, frag_len);
pos += frag_len;
}
wpabuf_free(data);
return ie;
}

View file

@ -0,0 +1,630 @@
/*
* Wi-Fi Protected Setup - attribute parsing
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "wps_i.h"
#ifndef CONFIG_WPS_STRICT
#define WPS_WORKAROUNDS
#endif /* CONFIG_WPS_STRICT */
static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
u8 id, u8 len, const u8 *pos)
{
wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
id, len);
switch (id) {
case WFA_ELEM_VERSION2:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
"%u", len);
return -1;
}
attr->version2 = pos;
break;
case WFA_ELEM_AUTHORIZEDMACS:
attr->authorized_macs = pos;
attr->authorized_macs_len = len;
break;
case WFA_ELEM_NETWORK_KEY_SHAREABLE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
"Shareable length %u", len);
return -1;
}
attr->network_key_shareable = pos;
break;
case WFA_ELEM_REQUEST_TO_ENROLL:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
"length %u", len);
return -1;
}
attr->request_to_enroll = pos;
break;
case WFA_ELEM_SETTINGS_DELAY_TIME:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
"Time length %u", len);
return -1;
}
attr->settings_delay_time = pos;
break;
default:
wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
"Extension subelement %u", id);
break;
}
return 0;
}
static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
u16 len)
{
const u8 *end = pos + len;
u8 id, elen;
while (pos + 2 < end) {
id = *pos++;
elen = *pos++;
if (pos + elen > end)
break;
if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
return -1;
pos += elen;
}
return 0;
}
static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
u16 len)
{
u32 vendor_id;
if (len < 3) {
wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
return 0;
}
vendor_id = WPA_GET_BE24(pos);
switch (vendor_id) {
case WPS_VENDOR_ID_WFA:
return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
}
/* Handle unknown vendor extensions */
wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
vendor_id);
if (len > WPS_MAX_VENDOR_EXT_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
len);
return -1;
}
if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
"attribute (max %d vendor extensions)",
MAX_WPS_PARSE_VENDOR_EXT);
return -1;
}
attr->vendor_ext[attr->num_vendor_ext] = pos;
attr->vendor_ext_len[attr->num_vendor_ext] = len;
attr->num_vendor_ext++;
return 0;
}
static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
const u8 *pos, u16 len)
{
switch (type) {
case ATTR_VERSION:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
len);
return -1;
}
attr->version = pos;
break;
case ATTR_MSG_TYPE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
"length %u", len);
return -1;
}
attr->msg_type = pos;
break;
case ATTR_ENROLLEE_NONCE:
if (len != WPS_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
"length %u", len);
return -1;
}
attr->enrollee_nonce = pos;
break;
case ATTR_REGISTRAR_NONCE:
if (len != WPS_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
"length %u", len);
return -1;
}
attr->registrar_nonce = pos;
break;
case ATTR_UUID_E:
if (len != WPS_UUID_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
len);
return -1;
}
attr->uuid_e = pos;
break;
case ATTR_UUID_R:
if (len != WPS_UUID_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
len);
return -1;
}
attr->uuid_r = pos;
break;
case ATTR_AUTH_TYPE_FLAGS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
"Type Flags length %u", len);
return -1;
}
attr->auth_type_flags = pos;
break;
case ATTR_ENCR_TYPE_FLAGS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
"Flags length %u", len);
return -1;
}
attr->encr_type_flags = pos;
break;
case ATTR_CONN_TYPE_FLAGS:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
"Flags length %u", len);
return -1;
}
attr->conn_type_flags = pos;
break;
case ATTR_CONFIG_METHODS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
"length %u", len);
return -1;
}
attr->config_methods = pos;
break;
case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
"Registrar Config Methods length %u", len);
return -1;
}
attr->sel_reg_config_methods = pos;
break;
case ATTR_PRIMARY_DEV_TYPE:
if (len != WPS_DEV_TYPE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
"Type length %u", len);
return -1;
}
attr->primary_dev_type = pos;
break;
case ATTR_RF_BANDS:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
"%u", len);
return -1;
}
attr->rf_bands = pos;
break;
case ATTR_ASSOC_STATE:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
"length %u", len);
return -1;
}
attr->assoc_state = pos;
break;
case ATTR_CONFIG_ERROR:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
"Error length %u", len);
return -1;
}
attr->config_error = pos;
break;
case ATTR_DEV_PASSWORD_ID:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
"ID length %u", len);
return -1;
}
attr->dev_password_id = pos;
break;
case ATTR_OOB_DEVICE_PASSWORD:
if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
"Password length %u", len);
return -1;
}
attr->oob_dev_password = pos;
break;
case ATTR_OS_VERSION:
if (len != 4) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
"%u", len);
return -1;
}
attr->os_version = pos;
break;
case ATTR_WPS_STATE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
"Setup State length %u", len);
return -1;
}
attr->wps_state = pos;
break;
case ATTR_AUTHENTICATOR:
if (len != WPS_AUTHENTICATOR_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
"length %u", len);
return -1;
}
attr->authenticator = pos;
break;
case ATTR_R_HASH1:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
len);
return -1;
}
attr->r_hash1 = pos;
break;
case ATTR_R_HASH2:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
len);
return -1;
}
attr->r_hash2 = pos;
break;
case ATTR_E_HASH1:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
len);
return -1;
}
attr->e_hash1 = pos;
break;
case ATTR_E_HASH2:
if (len != WPS_HASH_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
len);
return -1;
}
attr->e_hash2 = pos;
break;
case ATTR_R_SNONCE1:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
"%u", len);
return -1;
}
attr->r_snonce1 = pos;
break;
case ATTR_R_SNONCE2:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
"%u", len);
return -1;
}
attr->r_snonce2 = pos;
break;
case ATTR_E_SNONCE1:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
"%u", len);
return -1;
}
attr->e_snonce1 = pos;
break;
case ATTR_E_SNONCE2:
if (len != WPS_SECRET_NONCE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
"%u", len);
return -1;
}
attr->e_snonce2 = pos;
break;
case ATTR_KEY_WRAP_AUTH:
if (len != WPS_KWA_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
"Authenticator length %u", len);
return -1;
}
attr->key_wrap_auth = pos;
break;
case ATTR_AUTH_TYPE:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
"Type length %u", len);
return -1;
}
attr->auth_type = pos;
break;
case ATTR_ENCR_TYPE:
if (len != 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
"Type length %u", len);
return -1;
}
attr->encr_type = pos;
break;
case ATTR_NETWORK_INDEX:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
"length %u", len);
return -1;
}
attr->network_idx = pos;
break;
case ATTR_NETWORK_KEY_INDEX:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
"length %u", len);
return -1;
}
attr->network_key_idx = pos;
break;
case ATTR_MAC_ADDR:
if (len != ETH_ALEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
"length %u", len);
return -1;
}
attr->mac_addr = pos;
break;
case ATTR_KEY_PROVIDED_AUTO:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
"Automatically length %u", len);
return -1;
}
attr->key_prov_auto = pos;
break;
case ATTR_802_1X_ENABLED:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
"length %u", len);
return -1;
}
attr->dot1x_enabled = pos;
break;
case ATTR_SELECTED_REGISTRAR:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
" length %u", len);
return -1;
}
attr->selected_registrar = pos;
break;
case ATTR_REQUEST_TYPE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
"length %u", len);
return -1;
}
attr->request_type = pos;
break;
case ATTR_RESPONSE_TYPE:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
"length %u", len);
return -1;
}
attr->response_type = pos;
break;
case ATTR_MANUFACTURER:
attr->manufacturer = pos;
attr->manufacturer_len = len;
break;
case ATTR_MODEL_NAME:
attr->model_name = pos;
attr->model_name_len = len;
break;
case ATTR_MODEL_NUMBER:
attr->model_number = pos;
attr->model_number_len = len;
break;
case ATTR_SERIAL_NUMBER:
attr->serial_number = pos;
attr->serial_number_len = len;
break;
case ATTR_DEV_NAME:
attr->dev_name = pos;
attr->dev_name_len = len;
break;
case ATTR_PUBLIC_KEY:
attr->public_key = pos;
attr->public_key_len = len;
break;
case ATTR_ENCR_SETTINGS:
attr->encr_settings = pos;
attr->encr_settings_len = len;
break;
case ATTR_CRED:
if (attr->num_cred >= MAX_CRED_COUNT) {
wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
"attribute (max %d credentials)",
MAX_CRED_COUNT);
break;
}
attr->cred[attr->num_cred] = pos;
attr->cred_len[attr->num_cred] = len;
attr->num_cred++;
break;
case ATTR_SSID:
attr->ssid = pos;
attr->ssid_len = len;
break;
case ATTR_NETWORK_KEY:
attr->network_key = pos;
attr->network_key_len = len;
break;
case ATTR_EAP_TYPE:
attr->eap_type = pos;
attr->eap_type_len = len;
break;
case ATTR_EAP_IDENTITY:
attr->eap_identity = pos;
attr->eap_identity_len = len;
break;
case ATTR_AP_SETUP_LOCKED:
if (len != 1) {
wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
"length %u", len);
return -1;
}
attr->ap_setup_locked = pos;
break;
case ATTR_REQUESTED_DEV_TYPE:
if (len != WPS_DEV_TYPE_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
"Type length %u", len);
return -1;
}
if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
"Type attribute (max %u types)",
MAX_REQ_DEV_TYPE_COUNT);
break;
}
attr->req_dev_type[attr->num_req_dev_type] = pos;
attr->num_req_dev_type++;
break;
case ATTR_SECONDARY_DEV_TYPE_LIST:
if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
(len % WPS_DEV_TYPE_LEN) > 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
"Type length %u", len);
return -1;
}
attr->sec_dev_type_list = pos;
attr->sec_dev_type_list_len = len;
break;
case ATTR_VENDOR_EXT:
if (wps_parse_vendor_ext(attr, pos, len) < 0)
return -1;
break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
"len=%u", type, len);
break;
}
return 0;
}
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
{
const u8 *pos, *end;
u16 type, len;
u16 prev_type = 0;
os_memset(attr, 0, sizeof(*attr));
pos = wpabuf_head(msg);
end = pos + wpabuf_len(msg);
while (pos < end) {
if (end - pos < 4) {
wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
"%lu bytes remaining",
(unsigned long) (end - pos));
return -1;
}
type = WPA_GET_BE16(pos);
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
type, len);
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
#ifdef WPS_WORKAROUNDS
/*
* Some deployed APs seem to have a bug in encoding of
* Network Key attribute in the Credential attribute
* where they add an extra octet after the Network Key
* attribute at least when open network is being
* provisioned.
*/
if ((type & 0xff00) != 0x1000 &&
prev_type == ATTR_NETWORK_KEY) {
wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
"to skip unexpected octet after "
"Network Key");
pos -= 3;
continue;
}
#endif /* WPS_WORKAROUNDS */
return -1;
}
#ifdef WPS_WORKAROUNDS
if (type == 0 && len == 0) {
/*
* Mac OS X 10.6 seems to be adding 0x00 padding to the
* end of M1. Skip those to avoid interop issues.
*/
int i;
for (i = 0; i < end - pos; i++) {
if (pos[i])
break;
}
if (i == end - pos) {
wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
"unexpected message padding");
break;
}
}
#endif /* WPS_WORKAROUNDS */
if (wps_set_attr(attr, type, pos, len) < 0)
return -1;
prev_type = type;
pos += len;
}
return 0;
}

View file

@ -0,0 +1,335 @@
/*
* Wi-Fi Protected Setup - attribute processing
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/sha256.h"
#include "wps_i.h"
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
const struct wpabuf *msg)
{
u8 hash[SHA256_MAC_LEN];
const u8 *addr[2];
size_t len[2];
if (authenticator == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
"included");
return -1;
}
if (wps->last_msg == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
"validating authenticator");
return -1;
}
/* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
* (M_curr* is M_curr without the Authenticator attribute)
*/
addr[0] = wpabuf_head(wps->last_msg);
len[0] = wpabuf_len(wps->last_msg);
addr[1] = wpabuf_head(msg);
len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
return -1;
}
return 0;
}
int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
const u8 *key_wrap_auth)
{
u8 hash[SHA256_MAC_LEN];
const u8 *head;
size_t len;
if (key_wrap_auth == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
return -1;
}
head = wpabuf_head(msg);
len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
if (head + len != key_wrap_auth - 4) {
wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
"decrypted attribute");
return -1;
}
hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
return -1;
}
return 0;
}
static int wps_process_cred_network_idx(struct wps_credential *cred,
const u8 *idx)
{
if (idx == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
"Network Index");
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
return 0;
}
static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
size_t ssid_len)
{
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
return -1;
}
/* Remove zero-padding since some Registrar implementations seem to use
* hardcoded 32-octet length for this attribute */
while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
ssid_len--;
wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
if (ssid_len <= sizeof(cred->ssid)) {
os_memcpy(cred->ssid, ssid, ssid_len);
cred->ssid_len = ssid_len;
}
return 0;
}
static int wps_process_cred_auth_type(struct wps_credential *cred,
const u8 *auth_type)
{
if (auth_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
"Authentication Type");
return -1;
}
cred->auth_type = WPA_GET_BE16(auth_type);
wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
cred->auth_type);
return 0;
}
static int wps_process_cred_encr_type(struct wps_credential *cred,
const u8 *encr_type)
{
if (encr_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
"Encryption Type");
return -1;
}
cred->encr_type = WPA_GET_BE16(encr_type);
wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
cred->encr_type);
return 0;
}
static int wps_process_cred_network_key_idx(struct wps_credential *cred,
const u8 *key_idx)
{
if (key_idx == NULL)
return 0; /* optional attribute */
wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
cred->key_idx = *key_idx;
return 0;
}
static int wps_process_cred_network_key(struct wps_credential *cred,
const u8 *key, size_t key_len)
{
if (key == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
"Network Key");
if (cred->auth_type == WPS_AUTH_OPEN &&
cred->encr_type == WPS_ENCR_NONE) {
wpa_printf(MSG_DEBUG, "WPS: Workaround - Allow "
"missing mandatory Network Key attribute "
"for open network");
return 0;
}
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
if (key_len <= sizeof(cred->key)) {
os_memcpy(cred->key, key, key_len);
cred->key_len = key_len;
}
return 0;
}
static int wps_process_cred_mac_addr(struct wps_credential *cred,
const u8 *mac_addr)
{
if (mac_addr == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
"MAC Address");
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
return 0;
}
static int wps_process_cred_eap_type(struct wps_credential *cred,
const u8 *eap_type, size_t eap_type_len)
{
if (eap_type == NULL)
return 0; /* optional attribute */
wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
return 0;
}
static int wps_process_cred_eap_identity(struct wps_credential *cred,
const u8 *identity,
size_t identity_len)
{
if (identity == NULL)
return 0; /* optional attribute */
wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
identity, identity_len);
return 0;
}
static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
const u8 *key_prov_auto)
{
if (key_prov_auto == NULL)
return 0; /* optional attribute */
wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
*key_prov_auto);
return 0;
}
static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
const u8 *dot1x_enabled)
{
if (dot1x_enabled == NULL)
return 0; /* optional attribute */
wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
return 0;
}
static int wps_workaround_cred_key(struct wps_credential *cred)
{
if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
cred->key_len > 8 && cred->key_len < 64 &&
cred->key[cred->key_len - 1] == 0) {
#ifdef CONFIG_WPS_STRICT
wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
"forbidden NULL termination");
wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
cred->key, cred->key_len);
return -1;
#else /* CONFIG_WPS_STRICT */
/*
* A deployed external registrar is known to encode ASCII
* passphrases incorrectly. Remove the extra NULL termination
* to fix the encoding.
*/
wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
"termination from ASCII passphrase");
cred->key_len--;
#endif /* CONFIG_WPS_STRICT */
}
return 0;
}
int wps_process_cred(struct wps_parse_attr *attr,
struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: Process Credential");
/* TODO: support multiple Network Keys */
if (wps_process_cred_network_idx(cred, attr->network_idx) ||
wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
wps_process_cred_auth_type(cred, attr->auth_type) ||
wps_process_cred_encr_type(cred, attr->encr_type) ||
wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
wps_process_cred_network_key(cred, attr->network_key,
attr->network_key_len) ||
wps_process_cred_mac_addr(cred, attr->mac_addr) ||
wps_process_cred_eap_type(cred, attr->eap_type,
attr->eap_type_len) ||
wps_process_cred_eap_identity(cred, attr->eap_identity,
attr->eap_identity_len) ||
wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
return -1;
return wps_workaround_cred_key(cred);
}
int wps_process_ap_settings(struct wps_parse_attr *attr,
struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
os_memset(cred, 0, sizeof(*cred));
/* TODO: optional attributes New Password and Device Password ID */
if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
wps_process_cred_auth_type(cred, attr->auth_type) ||
wps_process_cred_encr_type(cred, attr->encr_type) ||
wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
wps_process_cred_network_key(cred, attr->network_key,
attr->network_key_len) ||
wps_process_cred_mac_addr(cred, attr->mac_addr))
return -1;
return wps_workaround_cred_key(cred);
}

View file

@ -0,0 +1,704 @@
/*
* Wi-Fi Protected Setup - common functionality
* Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/dh_group5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
const char *label, u8 *res, size_t res_len)
{
u8 i_buf[4], key_bits[4];
const u8 *addr[4];
size_t len[4];
int i, iter;
u8 hash[SHA256_MAC_LEN], *opos;
size_t left;
WPA_PUT_BE32(key_bits, res_len * 8);
addr[0] = i_buf;
len[0] = sizeof(i_buf);
addr[1] = label_prefix;
len[1] = label_prefix_len;
addr[2] = (const u8 *) label;
len[2] = os_strlen(label);
addr[3] = key_bits;
len[3] = sizeof(key_bits);
iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
opos = res;
left = res_len;
for (i = 1; i <= iter; i++) {
WPA_PUT_BE32(i_buf, i);
hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
if (i < iter) {
os_memcpy(opos, hash, SHA256_MAC_LEN);
opos += SHA256_MAC_LEN;
left -= SHA256_MAC_LEN;
} else
os_memcpy(opos, hash, left);
}
}
int wps_derive_keys(struct wps_data *wps)
{
struct wpabuf *pubkey, *dh_shared;
u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
const u8 *addr[3];
size_t len[3];
u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
if (wps->dh_privkey == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
return -1;
}
pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
if (pubkey == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
return -1;
}
wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
dh5_free(wps->dh_ctx);
wps->dh_ctx = NULL;
dh_shared = wpabuf_zeropad(dh_shared, 192);
if (dh_shared == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
return -1;
}
/* Own DH private key is not needed anymore */
wpabuf_free(wps->dh_privkey);
wps->dh_privkey = NULL;
wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
/* DHKey = SHA-256(g^AB mod p) */
addr[0] = wpabuf_head(dh_shared);
len[0] = wpabuf_len(dh_shared);
sha256_vector(1, addr, len, dhkey);
wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
wpabuf_free(dh_shared);
/* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
addr[0] = wps->nonce_e;
len[0] = WPS_NONCE_LEN;
addr[1] = wps->mac_addr_e;
len[1] = ETH_ALEN;
addr[2] = wps->nonce_r;
len[2] = WPS_NONCE_LEN;
hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
keys, sizeof(keys));
os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
WPS_EMSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
wps->authkey, WPS_AUTHKEY_LEN);
wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
return 0;
}
void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
size_t dev_passwd_len)
{
u8 hash[SHA256_MAC_LEN];
hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
(dev_passwd_len + 1) / 2, hash);
os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
dev_passwd + (dev_passwd_len + 1) / 2,
dev_passwd_len / 2, hash);
os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
dev_passwd, dev_passwd_len);
wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
}
struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
size_t encr_len)
{
struct wpabuf *decrypted;
const size_t block_size = 16;
size_t i;
u8 pad;
const u8 *pos;
/* AES-128-CBC */
if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
{
wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
return NULL;
}
decrypted = wpabuf_alloc(encr_len - block_size);
if (decrypted == NULL)
return NULL;
wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
wpabuf_len(decrypted))) {
wpabuf_free(decrypted);
return NULL;
}
wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
decrypted);
pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
pad = *pos;
if (pad > wpabuf_len(decrypted)) {
wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
wpabuf_free(decrypted);
return NULL;
}
for (i = 0; i < pad; i++) {
if (*pos-- != pad) {
wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
"string");
wpabuf_free(decrypted);
return NULL;
}
}
decrypted->used -= pad;
return decrypted;
}
/**
* wps_pin_checksum - Compute PIN checksum
* @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
* Returns: Checksum digit
*/
unsigned int wps_pin_checksum(unsigned int pin)
{
unsigned int accum = 0;
while (pin) {
accum += 3 * (pin % 10);
pin /= 10;
accum += pin % 10;
pin /= 10;
}
return (10 - accum % 10) % 10;
}
/**
* wps_pin_valid - Check whether a PIN has a valid checksum
* @pin: Eight digit PIN (i.e., including the checksum digit)
* Returns: 1 if checksum digit is valid, or 0 if not
*/
unsigned int wps_pin_valid(unsigned int pin)
{
return wps_pin_checksum(pin / 10) == (pin % 10);
}
/**
* wps_generate_pin - Generate a random PIN
* Returns: Eight digit PIN (i.e., including the checksum digit)
*/
unsigned int wps_generate_pin(void)
{
unsigned int val;
/* Generate seven random digits for the PIN */
if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
struct os_time now;
os_get_time(&now);
val = os_random() ^ now.sec ^ now.usec;
}
val %= 10000000;
/* Append checksum digit */
return val * 10 + wps_pin_checksum(val);
}
void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
u16 config_error, u16 error_indication)
{
union wps_event_data data;
if (wps->event_cb == NULL)
return;
os_memset(&data, 0, sizeof(data));
data.fail.msg = msg;
data.fail.config_error = config_error;
data.fail.error_indication = error_indication;
wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
}
void wps_success_event(struct wps_context *wps)
{
if (wps->event_cb == NULL)
return;
wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
}
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
{
union wps_event_data data;
if (wps->event_cb == NULL)
return;
os_memset(&data, 0, sizeof(data));
data.pwd_auth_fail.enrollee = enrollee;
data.pwd_auth_fail.part = part;
wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
}
void wps_pbc_overlap_event(struct wps_context *wps)
{
if (wps->event_cb == NULL)
return;
wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
}
void wps_pbc_timeout_event(struct wps_context *wps)
{
if (wps->event_cb == NULL)
return;
wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
}
#ifdef CONFIG_WPS_OOB
static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
{
struct wps_data data;
struct wpabuf *plain;
plain = wpabuf_alloc(500);
if (plain == NULL) {
wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
"credential");
return NULL;
}
os_memset(&data, 0, sizeof(data));
data.wps = wps;
data.auth_type = wps->auth_types;
data.encr_type = wps->encr_types;
if (wps_build_version(plain) ||
wps_build_cred(&data, plain) ||
wps_build_wfa_ext(plain, 0, NULL, 0)) {
wpabuf_free(plain);
return NULL;
}
return plain;
}
static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
{
struct wpabuf *data;
data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
if (data == NULL) {
wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
"device password attribute");
return NULL;
}
wpabuf_free(wps->oob_conf.dev_password);
wps->oob_conf.dev_password =
wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
if (wps->oob_conf.dev_password == NULL) {
wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
"device password");
wpabuf_free(data);
return NULL;
}
if (wps_build_version(data) ||
wps_build_oob_dev_password(data, wps) ||
wps_build_wfa_ext(data, 0, NULL, 0)) {
wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
"attribute error");
wpabuf_free(data);
return NULL;
}
return data;
}
static int wps_parse_oob_dev_pwd(struct wps_context *wps,
struct wpabuf *data)
{
struct oob_conf_data *oob_conf = &wps->oob_conf;
struct wps_parse_attr attr;
const u8 *pos;
if (wps_parse_msg(data, &attr) < 0 ||
attr.oob_dev_password == NULL) {
wpa_printf(MSG_ERROR, "WPS: OOB device password not found");
return -1;
}
pos = attr.oob_dev_password;
oob_conf->pubkey_hash =
wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
if (oob_conf->pubkey_hash == NULL) {
wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
"public key hash");
return -1;
}
pos += WPS_OOB_PUBKEY_HASH_LEN;
wps->oob_dev_pw_id = WPA_GET_BE16(pos);
pos += sizeof(wps->oob_dev_pw_id);
oob_conf->dev_password =
wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
if (oob_conf->dev_password == NULL) {
wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
"device password");
return -1;
}
wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
wpabuf_size(oob_conf->dev_password)),
wpabuf_size(oob_conf->dev_password), pos,
WPS_OOB_DEVICE_PASSWORD_LEN);
return 0;
}
static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
{
struct wpabuf msg;
struct wps_parse_attr attr;
size_t i;
if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
return -1;
}
for (i = 0; i < attr.num_cred; i++) {
struct wps_credential local_cred;
struct wps_parse_attr cattr;
os_memset(&local_cred, 0, sizeof(local_cred));
wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
if (wps_parse_msg(&msg, &cattr) < 0 ||
wps_process_cred(&cattr, &local_cred)) {
wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
"credential");
return -1;
}
wps->cred_cb(wps->cb_ctx, &local_cred);
}
return 0;
}
int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
int registrar)
{
struct wpabuf *data;
int ret, write_f, oob_method = wps->oob_conf.oob_method;
void *oob_priv;
write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar;
oob_priv = oob_dev->init_func(wps, oob_dev, registrar);
if (oob_priv == NULL) {
wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device");
return -1;
}
if (write_f) {
if (oob_method == OOB_METHOD_CRED)
data = wps_get_oob_cred(wps);
else
data = wps_get_oob_dev_pwd(wps);
ret = 0;
if (data == NULL || oob_dev->write_func(oob_priv, data) < 0)
ret = -1;
} else {
data = oob_dev->read_func(oob_priv);
if (data == NULL)
ret = -1;
else {
if (oob_method == OOB_METHOD_CRED)
ret = wps_parse_oob_cred(wps, data);
else
ret = wps_parse_oob_dev_pwd(wps, data);
}
}
wpabuf_free(data);
oob_dev->deinit_func(oob_priv);
if (ret < 0) {
wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data");
return -1;
}
return 0;
}
struct oob_device_data * wps_get_oob_device(char *device_type)
{
#ifdef CONFIG_WPS_UFD
if (os_strstr(device_type, "ufd") != NULL)
return &oob_ufd_device_data;
#endif /* CONFIG_WPS_UFD */
#ifdef CONFIG_WPS_NFC
if (os_strstr(device_type, "nfc") != NULL)
return &oob_nfc_device_data;
#endif /* CONFIG_WPS_NFC */
return NULL;
}
#ifdef CONFIG_WPS_NFC
struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name)
{
if (device_name == NULL)
return NULL;
#ifdef CONFIG_WPS_NFC_PN531
if (os_strstr(device_name, "pn531") != NULL)
return &oob_nfc_pn531_device_data;
#endif /* CONFIG_WPS_NFC_PN531 */
return NULL;
}
#endif /* CONFIG_WPS_NFC */
int wps_get_oob_method(char *method)
{
if (os_strstr(method, "pin-e") != NULL)
return OOB_METHOD_DEV_PWD_E;
if (os_strstr(method, "pin-r") != NULL)
return OOB_METHOD_DEV_PWD_R;
if (os_strstr(method, "cred") != NULL)
return OOB_METHOD_CRED;
return OOB_METHOD_UNKNOWN;
}
#endif /* CONFIG_WPS_OOB */
int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
{
const char *pos;
/* <categ>-<OUI>-<subcateg> */
WPA_PUT_BE16(dev_type, atoi(str));
pos = os_strchr(str, '-');
if (pos == NULL)
return -1;
pos++;
if (hexstr2bin(pos, &dev_type[2], 4))
return -1;
pos = os_strchr(pos, '-');
if (pos == NULL)
return -1;
pos++;
WPA_PUT_BE16(&dev_type[6], atoi(pos));
return 0;
}
char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
size_t buf_len)
{
int ret;
ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
WPA_GET_BE16(&dev_type[6]));
if (ret < 0 || (unsigned int) ret >= buf_len)
return NULL;
return buf;
}
void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
{
const u8 *addr[2];
size_t len[2];
u8 hash[SHA1_MAC_LEN];
u8 nsid[16] = {
0x52, 0x64, 0x80, 0xf8,
0xc9, 0x9b,
0x4b, 0xe5,
0xa6, 0x55,
0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
};
addr[0] = nsid;
len[0] = sizeof(nsid);
addr[1] = mac_addr;
len[1] = 6;
sha1_vector(2, addr, len, hash);
os_memcpy(uuid, hash, 16);
/* Version: 5 = named-based version using SHA-1 */
uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
/* Variant specified in RFC 4122 */
uuid[8] = 0x80 | (uuid[8] & 0x3f);
}
u16 wps_config_methods_str2bin(const char *str)
{
u16 methods = 0;
if (str == NULL) {
/* Default to enabling methods based on build configuration */
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
#ifdef CONFIG_WPS2
methods |= WPS_CONFIG_VIRT_DISPLAY;
#endif /* CONFIG_WPS2 */
#ifdef CONFIG_WPS_UFD
methods |= WPS_CONFIG_USBA;
#endif /* CONFIG_WPS_UFD */
#ifdef CONFIG_WPS_NFC
methods |= WPS_CONFIG_NFC_INTERFACE;
#endif /* CONFIG_WPS_NFC */
} else {
if (os_strstr(str, "usba"))
methods |= WPS_CONFIG_USBA;
if (os_strstr(str, "ethernet"))
methods |= WPS_CONFIG_ETHERNET;
if (os_strstr(str, "label"))
methods |= WPS_CONFIG_LABEL;
if (os_strstr(str, "display"))
methods |= WPS_CONFIG_DISPLAY;
if (os_strstr(str, "ext_nfc_token"))
methods |= WPS_CONFIG_EXT_NFC_TOKEN;
if (os_strstr(str, "int_nfc_token"))
methods |= WPS_CONFIG_INT_NFC_TOKEN;
if (os_strstr(str, "nfc_interface"))
methods |= WPS_CONFIG_NFC_INTERFACE;
if (os_strstr(str, "push_button"))
methods |= WPS_CONFIG_PUSHBUTTON;
if (os_strstr(str, "keypad"))
methods |= WPS_CONFIG_KEYPAD;
#ifdef CONFIG_WPS2
if (os_strstr(str, "virtual_display"))
methods |= WPS_CONFIG_VIRT_DISPLAY;
if (os_strstr(str, "physical_display"))
methods |= WPS_CONFIG_PHY_DISPLAY;
if (os_strstr(str, "virtual_push_button"))
methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
if (os_strstr(str, "physical_push_button"))
methods |= WPS_CONFIG_PHY_PUSHBUTTON;
#endif /* CONFIG_WPS2 */
}
return methods;
}
struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
{
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
msg = wpabuf_alloc(1000);
if (msg == NULL)
return NULL;
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_WSC_ACK) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_wfa_ext(msg, 0, NULL, 0)) {
wpabuf_free(msg);
return NULL;
}
return msg;
}
struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
{
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
msg = wpabuf_alloc(1000);
if (msg == NULL)
return NULL;
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_WSC_NACK) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_config_error(msg, wps->config_error) ||
wps_build_wfa_ext(msg, 0, NULL, 0)) {
wpabuf_free(msg);
return NULL;
}
return msg;
}

View file

@ -0,0 +1,336 @@
/*
* Wi-Fi Protected Setup - message definitions
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef WPS_DEFS_H
#define WPS_DEFS_H
#ifdef CONFIG_WPS_TESTING
extern int wps_version_number;
extern int wps_testing_dummy_cred;
#define WPS_VERSION wps_version_number
#else /* CONFIG_WPS_TESTING */
#ifdef CONFIG_WPS2
#define WPS_VERSION 0x20
#else /* CONFIG_WPS2 */
#define WPS_VERSION 0x10
#endif /* CONFIG_WPS2 */
#endif /* CONFIG_WPS_TESTING */
/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
#define WPS_DH_GROUP 5
#define WPS_UUID_LEN 16
#define WPS_NONCE_LEN 16
#define WPS_AUTHENTICATOR_LEN 8
#define WPS_AUTHKEY_LEN 32
#define WPS_KEYWRAPKEY_LEN 16
#define WPS_EMSK_LEN 32
#define WPS_PSK_LEN 16
#define WPS_SECRET_NONCE_LEN 16
#define WPS_HASH_LEN 32
#define WPS_KWA_LEN 8
#define WPS_MGMTAUTHKEY_LEN 32
#define WPS_MGMTENCKEY_LEN 16
#define WPS_MGMT_KEY_ID_LEN 16
#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
#define WPS_OOB_DEVICE_PASSWORD_LEN 32
#define WPS_OOB_PUBKEY_HASH_LEN 20
/* Attribute Types */
enum wps_attribute {
ATTR_AP_CHANNEL = 0x1001,
ATTR_ASSOC_STATE = 0x1002,
ATTR_AUTH_TYPE = 0x1003,
ATTR_AUTH_TYPE_FLAGS = 0x1004,
ATTR_AUTHENTICATOR = 0x1005,
ATTR_CONFIG_METHODS = 0x1008,
ATTR_CONFIG_ERROR = 0x1009,
ATTR_CONFIRM_URL4 = 0x100a,
ATTR_CONFIRM_URL6 = 0x100b,
ATTR_CONN_TYPE = 0x100c,
ATTR_CONN_TYPE_FLAGS = 0x100d,
ATTR_CRED = 0x100e,
ATTR_ENCR_TYPE = 0x100f,
ATTR_ENCR_TYPE_FLAGS = 0x1010,
ATTR_DEV_NAME = 0x1011,
ATTR_DEV_PASSWORD_ID = 0x1012,
ATTR_E_HASH1 = 0x1014,
ATTR_E_HASH2 = 0x1015,
ATTR_E_SNONCE1 = 0x1016,
ATTR_E_SNONCE2 = 0x1017,
ATTR_ENCR_SETTINGS = 0x1018,
ATTR_ENROLLEE_NONCE = 0x101a,
ATTR_FEATURE_ID = 0x101b,
ATTR_IDENTITY = 0x101c,
ATTR_IDENTITY_PROOF = 0x101d,
ATTR_KEY_WRAP_AUTH = 0x101e,
ATTR_KEY_ID = 0x101f,
ATTR_MAC_ADDR = 0x1020,
ATTR_MANUFACTURER = 0x1021,
ATTR_MSG_TYPE = 0x1022,
ATTR_MODEL_NAME = 0x1023,
ATTR_MODEL_NUMBER = 0x1024,
ATTR_NETWORK_INDEX = 0x1026,
ATTR_NETWORK_KEY = 0x1027,
ATTR_NETWORK_KEY_INDEX = 0x1028,
ATTR_NEW_DEVICE_NAME = 0x1029,
ATTR_NEW_PASSWORD = 0x102a,
ATTR_OOB_DEVICE_PASSWORD = 0x102c,
ATTR_OS_VERSION = 0x102d,
ATTR_POWER_LEVEL = 0x102f,
ATTR_PSK_CURRENT = 0x1030,
ATTR_PSK_MAX = 0x1031,
ATTR_PUBLIC_KEY = 0x1032,
ATTR_RADIO_ENABLE = 0x1033,
ATTR_REBOOT = 0x1034,
ATTR_REGISTRAR_CURRENT = 0x1035,
ATTR_REGISTRAR_ESTABLISHED = 0x1036,
ATTR_REGISTRAR_LIST = 0x1037,
ATTR_REGISTRAR_MAX = 0x1038,
ATTR_REGISTRAR_NONCE = 0x1039,
ATTR_REQUEST_TYPE = 0x103a,
ATTR_RESPONSE_TYPE = 0x103b,
ATTR_RF_BANDS = 0x103c,
ATTR_R_HASH1 = 0x103d,
ATTR_R_HASH2 = 0x103e,
ATTR_R_SNONCE1 = 0x103f,
ATTR_R_SNONCE2 = 0x1040,
ATTR_SELECTED_REGISTRAR = 0x1041,
ATTR_SERIAL_NUMBER = 0x1042,
ATTR_WPS_STATE = 0x1044,
ATTR_SSID = 0x1045,
ATTR_TOTAL_NETWORKS = 0x1046,
ATTR_UUID_E = 0x1047,
ATTR_UUID_R = 0x1048,
ATTR_VENDOR_EXT = 0x1049,
ATTR_VERSION = 0x104a,
ATTR_X509_CERT_REQ = 0x104b,
ATTR_X509_CERT = 0x104c,
ATTR_EAP_IDENTITY = 0x104d,
ATTR_MSG_COUNTER = 0x104e,
ATTR_PUBKEY_HASH = 0x104f,
ATTR_REKEY_KEY = 0x1050,
ATTR_KEY_LIFETIME = 0x1051,
ATTR_PERMITTED_CFG_METHODS = 0x1052,
ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
ATTR_PRIMARY_DEV_TYPE = 0x1054,
ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
ATTR_PORTABLE_DEV = 0x1056,
ATTR_AP_SETUP_LOCKED = 0x1057,
ATTR_APPLICATION_EXT = 0x1058,
ATTR_EAP_TYPE = 0x1059,
ATTR_IV = 0x1060,
ATTR_KEY_PROVIDED_AUTO = 0x1061,
ATTR_802_1X_ENABLED = 0x1062,
ATTR_APPSESSIONKEY = 0x1063,
ATTR_WEPTRANSMITKEY = 0x1064,
ATTR_REQUESTED_DEV_TYPE = 0x106a,
ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
};
#define WPS_VENDOR_ID_WFA 14122
/* WFA Vendor Extension subelements */
enum {
WFA_ELEM_VERSION2 = 0x00,
WFA_ELEM_AUTHORIZEDMACS = 0x01,
WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
};
/* Device Password ID */
enum wps_dev_password_id {
DEV_PW_DEFAULT = 0x0000,
DEV_PW_USER_SPECIFIED = 0x0001,
DEV_PW_MACHINE_SPECIFIED = 0x0002,
DEV_PW_REKEY = 0x0003,
DEV_PW_PUSHBUTTON = 0x0004,
DEV_PW_REGISTRAR_SPECIFIED = 0x0005
};
/* Message Type */
enum wps_msg_type {
WPS_Beacon = 0x01,
WPS_ProbeRequest = 0x02,
WPS_ProbeResponse = 0x03,
WPS_M1 = 0x04,
WPS_M2 = 0x05,
WPS_M2D = 0x06,
WPS_M3 = 0x07,
WPS_M4 = 0x08,
WPS_M5 = 0x09,
WPS_M6 = 0x0a,
WPS_M7 = 0x0b,
WPS_M8 = 0x0c,
WPS_WSC_ACK = 0x0d,
WPS_WSC_NACK = 0x0e,
WPS_WSC_DONE = 0x0f
};
/* Authentication Type Flags */
#define WPS_AUTH_OPEN 0x0001
#define WPS_AUTH_WPAPSK 0x0002
#define WPS_AUTH_SHARED 0x0004
#define WPS_AUTH_WPA 0x0008
#define WPS_AUTH_WPA2 0x0010
#define WPS_AUTH_WPA2PSK 0x0020
#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
/* Encryption Type Flags */
#define WPS_ENCR_NONE 0x0001
#define WPS_ENCR_WEP 0x0002
#define WPS_ENCR_TKIP 0x0004
#define WPS_ENCR_AES 0x0008
#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
WPS_ENCR_AES)
/* Configuration Error */
enum wps_config_error {
WPS_CFG_NO_ERROR = 0,
WPS_CFG_OOB_IFACE_READ_ERROR = 1,
WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
WPS_CFG_SIGNAL_TOO_WEAK = 5,
WPS_CFG_NETWORK_AUTH_FAILURE = 6,
WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
WPS_CFG_NO_DHCP_RESPONSE = 8,
WPS_CFG_FAILED_DHCP_CONFIG = 9,
WPS_CFG_IP_ADDR_CONFLICT = 10,
WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
WPS_CFG_ROGUE_SUSPECTED = 13,
WPS_CFG_DEVICE_BUSY = 14,
WPS_CFG_SETUP_LOCKED = 15,
WPS_CFG_MSG_TIMEOUT = 16,
WPS_CFG_REG_SESS_TIMEOUT = 17,
WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
};
/* Vendor specific Error Indication for WPS event messages */
enum wps_error_indication {
WPS_EI_NO_ERROR,
WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
WPS_EI_SECURITY_WEP_PROHIBITED,
NUM_WPS_EI_VALUES
};
/* RF Bands */
#define WPS_RF_24GHZ 0x01
#define WPS_RF_50GHZ 0x02
/* Config Methods */
#define WPS_CONFIG_USBA 0x0001
#define WPS_CONFIG_ETHERNET 0x0002
#define WPS_CONFIG_LABEL 0x0004
#define WPS_CONFIG_DISPLAY 0x0008
#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
#define WPS_CONFIG_NFC_INTERFACE 0x0040
#define WPS_CONFIG_PUSHBUTTON 0x0080
#define WPS_CONFIG_KEYPAD 0x0100
#ifdef CONFIG_WPS2
#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
#define WPS_CONFIG_VIRT_DISPLAY 0x2008
#define WPS_CONFIG_PHY_DISPLAY 0x4008
#endif /* CONFIG_WPS2 */
/* Connection Type Flags */
#define WPS_CONN_ESS 0x01
#define WPS_CONN_IBSS 0x02
/* Wi-Fi Protected Setup State */
enum wps_state {
WPS_STATE_NOT_CONFIGURED = 1,
WPS_STATE_CONFIGURED = 2
};
/* Association State */
enum wps_assoc_state {
WPS_ASSOC_NOT_ASSOC = 0,
WPS_ASSOC_CONN_SUCCESS = 1,
WPS_ASSOC_CFG_FAILURE = 2,
WPS_ASSOC_FAILURE = 3,
WPS_ASSOC_IP_FAILURE = 4
};
#define WPS_DEV_OUI_WFA 0x0050f204
enum wps_dev_categ {
WPS_DEV_COMPUTER = 1,
WPS_DEV_INPUT = 2,
WPS_DEV_PRINTER = 3,
WPS_DEV_CAMERA = 4,
WPS_DEV_STORAGE = 5,
WPS_DEV_NETWORK_INFRA = 6,
WPS_DEV_DISPLAY = 7,
WPS_DEV_MULTIMEDIA = 8,
WPS_DEV_GAMING = 9,
WPS_DEV_PHONE = 10
};
enum wps_dev_subcateg {
WPS_DEV_COMPUTER_PC = 1,
WPS_DEV_COMPUTER_SERVER = 2,
WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
WPS_DEV_PRINTER_PRINTER = 1,
WPS_DEV_PRINTER_SCANNER = 2,
WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
WPS_DEV_STORAGE_NAS = 1,
WPS_DEV_NETWORK_INFRA_AP = 1,
WPS_DEV_NETWORK_INFRA_ROUTER = 2,
WPS_DEV_NETWORK_INFRA_SWITCH = 3,
WPS_DEV_DISPLAY_TV = 1,
WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
WPS_DEV_DISPLAY_PROJECTOR = 3,
WPS_DEV_MULTIMEDIA_DAR = 1,
WPS_DEV_MULTIMEDIA_PVR = 2,
WPS_DEV_MULTIMEDIA_MCX = 3,
WPS_DEV_GAMING_XBOX = 1,
WPS_DEV_GAMING_XBOX360 = 2,
WPS_DEV_GAMING_PLAYSTATION = 3,
WPS_DEV_PHONE_WINDOWS_MOBILE = 1
};
/* Request Type */
enum wps_request_type {
WPS_REQ_ENROLLEE_INFO = 0,
WPS_REQ_ENROLLEE = 1,
WPS_REQ_REGISTRAR = 2,
WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
};
/* Response Type */
enum wps_response_type {
WPS_RESP_ENROLLEE_INFO = 0,
WPS_RESP_ENROLLEE = 1,
WPS_RESP_REGISTRAR = 2,
WPS_RESP_AP = 3
};
/* Walk Time for push button configuration (in seconds) */
#define WPS_PBC_WALK_TIME 120
#define WPS_MAX_AUTHORIZED_MACS 5
#endif /* WPS_DEFS_H */

View file

@ -0,0 +1,444 @@
/*
* Wi-Fi Protected Setup - device attributes
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
wpabuf_put_be16(msg, ATTR_MANUFACTURER);
len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
* attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, ' ');
return 0;
}
#endif /* CONFIG_WPS_STRICT */
wpabuf_put_be16(msg, len);
wpabuf_put_data(msg, dev->manufacturer, len);
return 0;
}
int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Model Name");
wpabuf_put_be16(msg, ATTR_MODEL_NAME);
len = dev->model_name ? os_strlen(dev->model_name) : 0;
#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
* attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, ' ');
return 0;
}
#endif /* CONFIG_WPS_STRICT */
wpabuf_put_be16(msg, len);
wpabuf_put_data(msg, dev->model_name, len);
return 0;
}
int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Model Number");
wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
len = dev->model_number ? os_strlen(dev->model_number) : 0;
#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
* attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, ' ');
return 0;
}
#endif /* CONFIG_WPS_STRICT */
wpabuf_put_be16(msg, len);
wpabuf_put_data(msg, dev->model_number, len);
return 0;
}
static int wps_build_serial_number(struct wps_device_data *dev,
struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
* attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, ' ');
return 0;
}
#endif /* CONFIG_WPS_STRICT */
wpabuf_put_be16(msg, len);
wpabuf_put_data(msg, dev->serial_number, len);
return 0;
}
int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type");
wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
return 0;
}
int wps_build_secondary_dev_type(struct wps_device_data *dev,
struct wpabuf *msg)
{
if (!dev->num_sec_dev_types)
return 0;
wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type");
wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
wpabuf_put_data(msg, dev->sec_dev_type,
WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
return 0;
}
int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
unsigned int num_req_dev_types,
const u8 *req_dev_types)
{
unsigned int i;
for (i = 0; i < num_req_dev_types; i++) {
wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
req_dev_types + i * WPS_DEV_TYPE_LEN,
WPS_DEV_TYPE_LEN);
wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
WPS_DEV_TYPE_LEN);
}
return 0;
}
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Device Name");
wpabuf_put_be16(msg, ATTR_DEV_NAME);
len = dev->device_name ? os_strlen(dev->device_name) : 0;
#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
* attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, ' ');
return 0;
}
#endif /* CONFIG_WPS_STRICT */
wpabuf_put_be16(msg, len);
wpabuf_put_data(msg, dev->device_name, len);
return 0;
}
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
{
if (wps_build_manufacturer(dev, msg) ||
wps_build_model_name(dev, msg) ||
wps_build_model_number(dev, msg) ||
wps_build_serial_number(dev, msg) ||
wps_build_primary_dev_type(dev, msg) ||
wps_build_dev_name(dev, msg))
return -1;
return 0;
}
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * OS Version");
wpabuf_put_be16(msg, ATTR_OS_VERSION);
wpabuf_put_be16(msg, 4);
wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
return 0;
}
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands);
wpabuf_put_be16(msg, ATTR_RF_BANDS);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, dev->rf_bands);
return 0;
}
int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
{
int i;
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
if (dev->vendor_ext[i] == NULL)
continue;
wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension",
wpabuf_head_u8(dev->vendor_ext[i]),
wpabuf_len(dev->vendor_ext[i]));
wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
wpabuf_put_buf(msg, dev->vendor_ext[i]);
}
return 0;
}
static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
size_t str_len)
{
if (str == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
return -1;
}
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
os_free(dev->manufacturer);
dev->manufacturer = os_malloc(str_len + 1);
if (dev->manufacturer == NULL)
return -1;
os_memcpy(dev->manufacturer, str, str_len);
dev->manufacturer[str_len] = '\0';
return 0;
}
static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
size_t str_len)
{
if (str == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
return -1;
}
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
os_free(dev->model_name);
dev->model_name = os_malloc(str_len + 1);
if (dev->model_name == NULL)
return -1;
os_memcpy(dev->model_name, str, str_len);
dev->model_name[str_len] = '\0';
return 0;
}
static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
size_t str_len)
{
if (str == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
return -1;
}
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
os_free(dev->model_number);
dev->model_number = os_malloc(str_len + 1);
if (dev->model_number == NULL)
return -1;
os_memcpy(dev->model_number, str, str_len);
dev->model_number[str_len] = '\0';
return 0;
}
static int wps_process_serial_number(struct wps_device_data *dev,
const u8 *str, size_t str_len)
{
if (str == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
return -1;
}
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
os_free(dev->serial_number);
dev->serial_number = os_malloc(str_len + 1);
if (dev->serial_number == NULL)
return -1;
os_memcpy(dev->serial_number, str, str_len);
dev->serial_number[str_len] = '\0';
return 0;
}
static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
size_t str_len)
{
if (str == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
return -1;
}
wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
os_free(dev->device_name);
dev->device_name = os_malloc(str_len + 1);
if (dev->device_name == NULL)
return -1;
os_memcpy(dev->device_name, str, str_len);
dev->device_name[str_len] = '\0';
return 0;
}
static int wps_process_primary_dev_type(struct wps_device_data *dev,
const u8 *dev_type)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
char devtype[WPS_DEV_TYPE_BUFSIZE];
#endif /* CONFIG_NO_STDOUT_DEBUG */
if (dev_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
return -1;
}
os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s",
wps_dev_type_bin2str(dev->pri_dev_type, devtype,
sizeof(devtype)));
return 0;
}
int wps_process_device_attrs(struct wps_device_data *dev,
struct wps_parse_attr *attr)
{
if (wps_process_manufacturer(dev, attr->manufacturer,
attr->manufacturer_len) ||
wps_process_model_name(dev, attr->model_name,
attr->model_name_len) ||
wps_process_model_number(dev, attr->model_number,
attr->model_number_len) ||
wps_process_serial_number(dev, attr->serial_number,
attr->serial_number_len) ||
wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
return -1;
return 0;
}
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
{
if (ver == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
return -1;
}
dev->os_version = WPA_GET_BE32(ver);
wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
return 0;
}
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
{
if (bands == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
return -1;
}
dev->rf_bands = *bands;
wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
return 0;
}
void wps_device_data_dup(struct wps_device_data *dst,
const struct wps_device_data *src)
{
if (src->device_name)
dst->device_name = os_strdup(src->device_name);
if (src->manufacturer)
dst->manufacturer = os_strdup(src->manufacturer);
if (src->model_name)
dst->model_name = os_strdup(src->model_name);
if (src->model_number)
dst->model_number = os_strdup(src->model_number);
if (src->serial_number)
dst->serial_number = os_strdup(src->serial_number);
os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
dst->os_version = src->os_version;
dst->rf_bands = src->rf_bands;
}
void wps_device_data_free(struct wps_device_data *dev)
{
os_free(dev->device_name);
dev->device_name = NULL;
os_free(dev->manufacturer);
dev->manufacturer = NULL;
os_free(dev->model_name);
dev->model_name = NULL;
os_free(dev->model_number);
dev->model_number = NULL;
os_free(dev->serial_number);
dev->serial_number = NULL;
}

View file

@ -0,0 +1,44 @@
/*
* Wi-Fi Protected Setup - device attributes
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef WPS_DEV_ATTR_H
#define WPS_DEV_ATTR_H
struct wps_parse_attr;
int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_primary_dev_type(struct wps_device_data *dev,
struct wpabuf *msg);
int wps_build_secondary_dev_type(struct wps_device_data *dev,
struct wpabuf *msg);
int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_process_device_attrs(struct wps_device_data *dev,
struct wps_parse_attr *attr);
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
void wps_device_data_dup(struct wps_device_data *dst,
const struct wps_device_data *src);
void wps_device_data_free(struct wps_device_data *dev);
int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
unsigned int num_req_dev_types,
const u8 *req_dev_types);
#endif /* WPS_DEV_ATTR_H */

File diff suppressed because it is too large Load diff

1959
hostapd-0.8/src/wps/wps_er.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,117 @@
/*
* Wi-Fi Protected Setup - External Registrar
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef WPS_ER_H
#define WPS_ER_H
#include "utils/list.h"
struct wps_er_sta {
struct dl_list list;
struct wps_er_ap *ap;
u8 addr[ETH_ALEN];
u16 config_methods;
u8 uuid[WPS_UUID_LEN];
u8 pri_dev_type[8];
u16 dev_passwd_id;
int m1_received;
char *manufacturer;
char *model_name;
char *model_number;
char *serial_number;
char *dev_name;
struct wps_data *wps;
struct http_client *http;
struct wps_credential *cred;
};
struct wps_er_ap {
struct dl_list list;
struct wps_er *er;
struct dl_list sta; /* list of STAs/Enrollees using this AP */
struct in_addr addr;
char *location;
struct http_client *http;
struct wps_data *wps;
u8 uuid[WPS_UUID_LEN];
u8 pri_dev_type[8];
u8 wps_state;
u8 mac_addr[ETH_ALEN];
char *friendly_name;
char *manufacturer;
char *manufacturer_url;
char *model_description;
char *model_name;
char *model_number;
char *model_url;
char *serial_number;
char *udn;
char *upc;
char *scpd_url;
char *control_url;
char *event_sub_url;
int subscribed;
u8 sid[WPS_UUID_LEN];
unsigned int id;
struct wps_credential *ap_settings;
void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
};
struct wps_er_ap_settings {
struct dl_list list;
u8 uuid[WPS_UUID_LEN];
struct wps_credential ap_settings;
};
struct wps_er {
struct wps_context *wps;
char ifname[17];
u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
char *ip_addr_text; /* IP address of network i.f. we use */
unsigned ip_addr; /* IP address of network i.f. we use (host order) */
int multicast_sd;
int ssdp_sd;
struct dl_list ap;
struct dl_list ap_unsubscribing;
struct dl_list ap_settings;
struct http_server *http_srv;
int http_port;
unsigned int next_ap_id;
unsigned int event_id;
int deinitializing;
void (*deinit_done_cb)(void *ctx);
void *deinit_done_ctx;
struct in_addr filter_addr;
int skip_set_sel_reg;
const u8 *set_sel_reg_uuid_filter;
};
/* wps_er.c */
void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
const char *location, int max_age);
void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr);
/* wps_er_ssdp.c */
int wps_er_ssdp_init(struct wps_er *er);
void wps_er_ssdp_deinit(struct wps_er *er);
void wps_er_send_ssdp_msearch(struct wps_er *er);
#endif /* WPS_ER_H */

View file

@ -0,0 +1,211 @@
/*
* Wi-Fi Protected Setup - External Registrar (SSDP)
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "uuid.h"
#include "eloop.h"
#include "wps_i.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
#include "wps_er.h"
static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
{
struct wps_er *er = eloop_ctx;
struct sockaddr_in addr; /* client address */
socklen_t addr_len;
int nread;
char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
int wfa = 0, byebye = 0;
int max_age = -1;
char *location = NULL;
u8 uuid[WPS_UUID_LEN];
addr_len = sizeof(addr);
nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
(struct sockaddr *) &addr, &addr_len);
if (nread <= 0)
return;
buf[nread] = '\0';
if (er->filter_addr.s_addr &&
er->filter_addr.s_addr != addr.sin_addr.s_addr)
return;
wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
inet_ntoa(addr.sin_addr));
wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
(u8 *) buf, nread);
if (sd == er->multicast_sd) {
/* Reply to M-SEARCH */
if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
return; /* unexpected response header */
} else {
/* Unsolicited message (likely NOTIFY or M-SEARCH) */
if (os_strncmp(buf, "NOTIFY ", 7) != 0)
return; /* only process notifications */
}
os_memset(uuid, 0, sizeof(uuid));
for (start = buf; start && *start; start = pos) {
pos = os_strchr(start, '\n');
if (pos) {
if (pos[-1] == '\r')
pos[-1] = '\0';
*pos++ = '\0';
}
if (os_strstr(start, "schemas-wifialliance-org:device:"
"WFADevice:1"))
wfa = 1;
if (os_strstr(start, "schemas-wifialliance-org:service:"
"WFAWLANConfig:1"))
wfa = 1;
if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
start += 9;
while (*start == ' ')
start++;
location = start;
} else if (os_strncasecmp(start, "NTS:", 4) == 0) {
if (os_strstr(start, "ssdp:byebye"))
byebye = 1;
} else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
start += 9;
while (*start == ' ')
start++;
pos2 = os_strstr(start, "max-age=");
if (pos2 == NULL)
continue;
pos2 += 8;
max_age = atoi(pos2);
} else if (os_strncasecmp(start, "USN:", 4) == 0) {
start += 4;
pos2 = os_strstr(start, "uuid:");
if (pos2) {
pos2 += 5;
while (*pos2 == ' ')
pos2++;
if (uuid_str2bin(pos2, uuid) < 0) {
wpa_printf(MSG_DEBUG, "WPS ER: "
"Invalid UUID in USN: %s",
pos2);
return;
}
}
}
}
if (!wfa)
return; /* Not WPS advertisement/reply */
if (byebye) {
wps_er_ap_cache_settings(er, &addr.sin_addr);
wps_er_ap_remove(er, &addr.sin_addr);
return;
}
if (!location)
return; /* Unknown location */
if (max_age < 1)
return; /* No max-age reported */
wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
"(packet source: %s max-age: %d)",
location, inet_ntoa(addr.sin_addr), max_age);
wps_er_ap_add(er, uuid, &addr.sin_addr, location, max_age);
}
void wps_er_send_ssdp_msearch(struct wps_er *er)
{
struct wpabuf *msg;
struct sockaddr_in dest;
msg = wpabuf_alloc(500);
if (msg == NULL)
return;
wpabuf_put_str(msg,
"M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\n"
"MX: 3\r\n"
"ST: urn:schemas-wifialliance-org:device:WFADevice:1"
"\r\n"
"\r\n");
os_memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
dest.sin_port = htons(UPNP_MULTICAST_PORT);
if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
(struct sockaddr *) &dest, sizeof(dest)) < 0)
wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
"%d (%s)", errno, strerror(errno));
wpabuf_free(msg);
}
int wps_er_ssdp_init(struct wps_er *er)
{
if (add_ssdp_network(er->ifname)) {
wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for "
"SSDP");
return -1;
}
er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr);
if (er->multicast_sd < 0) {
wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket "
"for SSDP");
return -1;
}
er->ssdp_sd = ssdp_listener_open();
if (er->ssdp_sd < 0) {
wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener "
"socket");
return -1;
}
if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
wps_er_ssdp_rx, er, NULL) ||
eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
wps_er_ssdp_rx, er, NULL))
return -1;
wps_er_send_ssdp_msearch(er);
return 0;
}
void wps_er_ssdp_deinit(struct wps_er *er)
{
if (er->multicast_sd >= 0) {
eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
close(er->multicast_sd);
}
if (er->ssdp_sd >= 0) {
eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
close(er->ssdp_sd);
}
}

301
hostapd-0.8/src/wps/wps_i.h Normal file
View file

@ -0,0 +1,301 @@
/*
* Wi-Fi Protected Setup - internal definitions
* Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#ifndef WPS_I_H
#define WPS_I_H
#include "wps.h"
/**
* struct wps_data - WPS registration protocol data
*
* This data is stored at the EAP-WSC server/peer method and it is kept for a
* single registration protocol run.
*/
struct wps_data {
/**
* wps - Pointer to long term WPS context
*/
struct wps_context *wps;
/**
* registrar - Whether this end is a Registrar
*/
int registrar;
/**
* er - Whether the local end is an external registrar
*/
int er;
enum {
/* Enrollee states */
SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
SEND_WSC_NACK,
/* Registrar states */
RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
} state;
u8 uuid_e[WPS_UUID_LEN];
u8 uuid_r[WPS_UUID_LEN];
u8 mac_addr_e[ETH_ALEN];
u8 nonce_e[WPS_NONCE_LEN];
u8 nonce_r[WPS_NONCE_LEN];
u8 psk1[WPS_PSK_LEN];
u8 psk2[WPS_PSK_LEN];
u8 snonce[2 * WPS_SECRET_NONCE_LEN];
u8 peer_hash1[WPS_HASH_LEN];
u8 peer_hash2[WPS_HASH_LEN];
struct wpabuf *dh_privkey;
struct wpabuf *dh_pubkey_e;
struct wpabuf *dh_pubkey_r;
u8 authkey[WPS_AUTHKEY_LEN];
u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
u8 emsk[WPS_EMSK_LEN];
struct wpabuf *last_msg;
u8 *dev_password;
size_t dev_password_len;
u16 dev_pw_id;
int pbc;
/**
* request_type - Request Type attribute from (Re)AssocReq
*/
u8 request_type;
/**
* encr_type - Available encryption types
*/
u16 encr_type;
/**
* auth_type - Available authentication types
*/
u16 auth_type;
u8 *new_psk;
size_t new_psk_len;
int wps_pin_revealed;
struct wps_credential cred;
struct wps_device_data peer_dev;
/**
* config_error - Configuration Error value to be used in NACK
*/
u16 config_error;
u16 error_indication;
int ext_reg;
int int_reg;
struct wps_credential *new_ap_settings;
void *dh_ctx;
void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred);
void *ap_settings_cb_ctx;
struct wps_credential *use_cred;
int use_psk_key;
u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
* 00:00:00:00:00:00 if not a P2p client */
};
struct wps_parse_attr {
/* fixed length fields */
const u8 *version; /* 1 octet */
const u8 *version2; /* 1 octet */
const u8 *msg_type; /* 1 octet */
const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
const u8 *auth_type_flags; /* 2 octets */
const u8 *encr_type_flags; /* 2 octets */
const u8 *conn_type_flags; /* 1 octet */
const u8 *config_methods; /* 2 octets */
const u8 *sel_reg_config_methods; /* 2 octets */
const u8 *primary_dev_type; /* 8 octets */
const u8 *rf_bands; /* 1 octet */
const u8 *assoc_state; /* 2 octets */
const u8 *config_error; /* 2 octets */
const u8 *dev_password_id; /* 2 octets */
const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
* octets */
const u8 *os_version; /* 4 octets */
const u8 *wps_state; /* 1 octet */
const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
const u8 *auth_type; /* 2 octets */
const u8 *encr_type; /* 2 octets */
const u8 *network_idx; /* 1 octet */
const u8 *network_key_idx; /* 1 octet */
const u8 *mac_addr; /* ETH_ALEN (6) octets */
const u8 *key_prov_auto; /* 1 octet (Bool) */
const u8 *dot1x_enabled; /* 1 octet (Bool) */
const u8 *selected_registrar; /* 1 octet (Bool) */
const u8 *request_type; /* 1 octet */
const u8 *response_type; /* 1 octet */
const u8 *ap_setup_locked; /* 1 octet */
const u8 *settings_delay_time; /* 1 octet */
const u8 *network_key_shareable; /* 1 octet (Bool) */
const u8 *request_to_enroll; /* 1 octet (Bool) */
/* variable length fields */
const u8 *manufacturer;
size_t manufacturer_len;
const u8 *model_name;
size_t model_name_len;
const u8 *model_number;
size_t model_number_len;
const u8 *serial_number;
size_t serial_number_len;
const u8 *dev_name;
size_t dev_name_len;
const u8 *public_key;
size_t public_key_len;
const u8 *encr_settings;
size_t encr_settings_len;
const u8 *ssid; /* <= 32 octets */
size_t ssid_len;
const u8 *network_key; /* <= 64 octets */
size_t network_key_len;
const u8 *eap_type; /* <= 8 octets */
size_t eap_type_len;
const u8 *eap_identity; /* <= 64 octets */
size_t eap_identity_len;
const u8 *authorized_macs; /* <= 30 octets */
size_t authorized_macs_len;
const u8 *sec_dev_type_list; /* <= 128 octets */
size_t sec_dev_type_list_len;
/* attributes that can occur multiple times */
#define MAX_CRED_COUNT 10
const u8 *cred[MAX_CRED_COUNT];
size_t cred_len[MAX_CRED_COUNT];
size_t num_cred;
#define MAX_REQ_DEV_TYPE_COUNT 10
const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
size_t num_req_dev_type;
const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
size_t num_vendor_ext;
};
/* wps_common.c */
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
const char *label, u8 *res, size_t res_len);
int wps_derive_keys(struct wps_data *wps);
void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
size_t dev_passwd_len);
struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
size_t encr_len);
void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
u16 config_error, u16 error_indication);
void wps_success_event(struct wps_context *wps);
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
void wps_pbc_overlap_event(struct wps_context *wps);
void wps_pbc_timeout_event(struct wps_context *wps);
extern struct oob_device_data oob_ufd_device_data;
extern struct oob_device_data oob_nfc_device_data;
extern struct oob_nfc_device_data oob_nfc_pn531_device_data;
struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
/* wps_attr_parse.c */
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
/* wps_attr_build.c */
int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type);
int wps_build_config_methods(struct wpabuf *msg, u16 methods);
int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid);
int wps_build_dev_password_id(struct wpabuf *msg, u16 id);
int wps_build_config_error(struct wpabuf *msg, u16 err);
int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
struct wpabuf *plain);
int wps_build_version(struct wpabuf *msg);
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
const u8 *auth_macs, size_t auth_macs_count);
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
/* wps_attr_process.c */
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
const struct wpabuf *msg);
int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
const u8 *key_wrap_auth);
int wps_process_cred(struct wps_parse_attr *attr,
struct wps_credential *cred);
int wps_process_ap_settings(struct wps_parse_attr *attr,
struct wps_credential *cred);
/* wps_enrollee.c */
struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
enum wsc_op_code *op_code);
enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
enum wsc_op_code op_code,
const struct wpabuf *msg);
/* wps_registrar.c */
struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
enum wsc_op_code *op_code);
enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
enum wsc_op_code op_code,
const struct wpabuf *msg);
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
int wps_device_store(struct wps_registrar *reg,
struct wps_device_data *dev, const u8 *uuid);
void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
int wps_registrar_pbc_overlap(struct wps_registrar *reg,
const u8 *addr, const u8 *uuid_e);
/* ndef.c */
struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
#endif /* WPS_I_H */

View file

@ -0,0 +1,117 @@
/*
* NFC routines for Wi-Fi Protected Setup
* Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "wps/wps.h"
#include "wps_i.h"
struct wps_nfc_data {
struct oob_nfc_device_data *oob_nfc_dev;
};
static void * init_nfc(struct wps_context *wps,
struct oob_device_data *oob_dev, int registrar)
{
struct oob_nfc_device_data *oob_nfc_dev;
struct wps_nfc_data *data;
oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name);
if (oob_nfc_dev == NULL) {
wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)",
oob_dev->device_name);
return NULL;
}
if (oob_nfc_dev->init_func(oob_dev->device_path) < 0)
return NULL;
data = os_zalloc(sizeof(*data));
if (data == NULL) {
wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
"nfc data area");
return NULL;
}
data->oob_nfc_dev = oob_nfc_dev;
return data;
}
static struct wpabuf * read_nfc(void *priv)
{
struct wps_nfc_data *data = priv;
struct wpabuf *wifi, *buf;
char *raw_data;
size_t len;
raw_data = data->oob_nfc_dev->read_func(&len);
if (raw_data == NULL)
return NULL;
wifi = wpabuf_alloc_copy(raw_data, len);
os_free(raw_data);
if (wifi == NULL) {
wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
"nfc read area");
return NULL;
}
buf = ndef_parse_wifi(wifi);
wpabuf_free(wifi);
if (buf == NULL)
wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap");
return buf;
}
static int write_nfc(void *priv, struct wpabuf *buf)
{
struct wps_nfc_data *data = priv;
struct wpabuf *wifi;
int ret;
wifi = ndef_build_wifi(buf);
if (wifi == NULL) {
wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap");
return -1;
}
ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi),
wpabuf_len(wifi));
wpabuf_free(wifi);
return ret;
}
static void deinit_nfc(void *priv)
{
struct wps_nfc_data *data = priv;
data->oob_nfc_dev->deinit_func();
os_free(data);
}
struct oob_device_data oob_nfc_device_data = {
.device_name = NULL,
.device_path = NULL,
.init_func = init_nfc,
.read_func = read_nfc,
.write_func = write_nfc,
.deinit_func = deinit_nfc,
};

View file

@ -0,0 +1,113 @@
/*
* NFC PN531 routines for Wi-Fi Protected Setup
* Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "wps/wps.h"
#include "wps_i.h"
#include "WpsNfcType.h"
#include "WpsNfc.h"
static int init_nfc_pn531(char *path)
{
u32 ret;
ret = WpsNfcInit();
if (ret != WPS_NFCLIB_ERR_SUCCESS) {
wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize "
"NFC Library: 0x%08x", ret);
return -1;
}
ret = WpsNfcOpenDevice((int8 *) path);
if (ret != WPS_NFCLIB_ERR_SUCCESS) {
wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open "
"NFC Device(%s): 0x%08x", path, ret);
goto fail;
}
ret = WpsNfcTokenDiscovery();
if (ret != WPS_NFCLIB_ERR_SUCCESS) {
wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover "
"token: 0x%08x", ret);
WpsNfcCloseDevice();
goto fail;
}
return 0;
fail:
WpsNfcDeinit();
return -1;
}
static void * read_nfc_pn531(size_t *size)
{
uint32 len;
u32 ret;
int8 *data;
ret = WpsNfcRawReadToken(&data, &len);
if (ret != WPS_NFCLIB_ERR_SUCCESS) {
wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x",
ret);
return NULL;
}
*size = len;
return data;
}
static int write_nfc_pn531(void *data, size_t len)
{
u32 ret;
ret = WpsNfcRawWriteToken(data, len);
if (ret != WPS_NFCLIB_ERR_SUCCESS) {
wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x",
ret);
return -1;
}
return 0;
}
static void deinit_nfc_pn531(void)
{
u32 ret;
ret = WpsNfcCloseDevice();
if (ret != WPS_NFCLIB_ERR_SUCCESS)
wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close "
"NFC Device: 0x%08x", ret);
ret = WpsNfcDeinit();
if (ret != WPS_NFCLIB_ERR_SUCCESS)
wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize "
"NFC Library: 0x%08x", ret);
}
struct oob_nfc_device_data oob_nfc_pn531_device_data = {
.init_func = init_nfc_pn531,
.read_func = read_nfc_pn531,
.write_func = write_nfc_pn531,
.deinit_func = deinit_nfc_pn531,
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
/*
* UFD routines for Wi-Fi Protected Setup
* Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>
#include "wps/wps.h"
#include "wps/wps_i.h"
#ifdef CONFIG_NATIVE_WINDOWS
#define UFD_DIR1 "%s\\SMRTNTKY"
#define UFD_DIR2 UFD_DIR1 "\\WFAWSC"
#define UFD_FILE UFD_DIR2 "\\%s"
#else /* CONFIG_NATIVE_WINDOWS */
#define UFD_DIR1 "%s/SMRTNTKY"
#define UFD_DIR2 UFD_DIR1 "/WFAWSC"
#define UFD_FILE UFD_DIR2 "/%s"
#endif /* CONFIG_NATIVE_WINDOWS */
struct wps_ufd_data {
int ufd_fd;
};
static int dev_pwd_e_file_filter(const struct dirent *entry)
{
unsigned int prefix;
char ext[5];
if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2)
return 0;
if (prefix == 0)
return 0;
if (os_strcasecmp(ext, "WFA") != 0)
return 0;
return 1;
}
static int wps_get_dev_pwd_e_file_name(char *path, char *file_name)
{
struct dirent **namelist;
int i, file_num;
file_num = scandir(path, &namelist, &dev_pwd_e_file_filter,
alphasort);
if (file_num < 0) {
wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)",
errno, strerror(errno));
return -1;
}
if (file_num == 0) {
wpa_printf(MSG_ERROR, "WPS: OOB file not found");
os_free(namelist);
return -1;
}
os_strlcpy(file_name, namelist[0]->d_name, 13);
for (i = 0; i < file_num; i++)
os_free(namelist[i]);
os_free(namelist);
return 0;
}
static int get_file_name(struct wps_context *wps, int registrar,
const char *path, char *file_name)
{
switch (wps->oob_conf.oob_method) {
case OOB_METHOD_CRED:
os_snprintf(file_name, 13, "00000000.WSC");
break;
case OOB_METHOD_DEV_PWD_E:
if (registrar) {
char temp[128];
os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0)
return -1;
} else {
u8 *mac_addr = wps->dev.mac_addr;
os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA",
mac_addr[2], mac_addr[3], mac_addr[4],
mac_addr[5]);
}
break;
case OOB_METHOD_DEV_PWD_R:
os_snprintf(file_name, 13, "00000000.WFA");
break;
default:
wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method");
return -1;
}
return 0;
}
static int ufd_mkdir(const char *path)
{
if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) {
wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory "
"'%s': %d (%s)", path, errno, strerror(errno));
return -1;
}
return 0;
}
static void * init_ufd(struct wps_context *wps,
struct oob_device_data *oob_dev, int registrar)
{
int write_f;
char temp[128];
char *path = oob_dev->device_path;
char filename[13];
struct wps_ufd_data *data;
int ufd_fd;
if (path == NULL)
return NULL;
write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ?
!registrar : registrar;
if (get_file_name(wps, registrar, path, filename) < 0) {
wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name");
return NULL;
}
if (write_f) {
os_snprintf(temp, sizeof(temp), UFD_DIR1, path);
if (ufd_mkdir(temp))
return NULL;
os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
if (ufd_mkdir(temp))
return NULL;
}
os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename);
if (write_f)
ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
else
ufd_fd = open(temp, O_RDONLY);
if (ufd_fd < 0) {
wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s",
temp, strerror(errno));
return NULL;
}
data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->ufd_fd = ufd_fd;
return data;
}
static struct wpabuf * read_ufd(void *priv)
{
struct wps_ufd_data *data = priv;
struct wpabuf *buf;
struct stat s;
size_t file_size;
if (fstat(data->ufd_fd, &s) < 0) {
wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size");
return NULL;
}
file_size = s.st_size;
buf = wpabuf_alloc(file_size);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read "
"buffer");
return NULL;
}
if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) !=
(int) file_size) {
wpabuf_free(buf);
wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read");
return NULL;
}
wpabuf_put(buf, file_size);
return buf;
}
static int write_ufd(void *priv, struct wpabuf *buf)
{
struct wps_ufd_data *data = priv;
if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) !=
(int) wpabuf_len(buf)) {
wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write");
return -1;
}
return 0;
}
static void deinit_ufd(void *priv)
{
struct wps_ufd_data *data = priv;
close(data->ufd_fd);
os_free(data);
}
struct oob_device_data oob_ufd_device_data = {
.device_name = NULL,
.device_path = NULL,
.init_func = init_ufd,
.read_func = read_ufd,
.write_func = write_ufd,
.deinit_func = deinit_ufd,
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
/*
* UPnP WPS Device
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
#ifndef WPS_UPNP_H
#define WPS_UPNP_H
struct upnp_wps_device_sm;
struct wps_context;
struct wps_data;
struct upnp_wps_peer {
struct wps_data *wps;
};
enum upnp_wps_wlanevent_type {
UPNP_WPS_WLANEVENT_TYPE_PROBE = 1,
UPNP_WPS_WLANEVENT_TYPE_EAP = 2
};
struct upnp_wps_device_ctx {
int (*rx_req_put_wlan_response)(
void *priv, enum upnp_wps_wlanevent_type ev_type,
const u8 *mac_addr, const struct wpabuf *msg,
enum wps_msg_type msg_type);
char *ap_pin;
};
struct upnp_wps_device_sm *
upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
void *priv, char *net_if);
void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv);
int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
const u8 from_mac_addr[ETH_ALEN],
enum upnp_wps_wlanevent_type ev_type,
const struct wpabuf *msg);
int upnp_wps_subscribers(struct upnp_wps_device_sm *sm);
int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin);
#endif /* WPS_UPNP_H */

View file

@ -0,0 +1,91 @@
/*
* Wi-Fi Protected Setup - UPnP AP functionality
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "uuid.h"
#include "wps_i.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct subscription *s = eloop_ctx;
wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out");
s->selected_registrar = 0;
wps_registrar_selected_registrar_changed(s->reg);
}
int upnp_er_set_selected_registrar(struct wps_registrar *reg,
struct subscription *s,
const struct wpabuf *msg)
{
struct wps_parse_attr attr;
wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
msg);
if (wps_validate_upnp_set_selected_registrar(msg) < 0)
return -1;
if (wps_parse_msg(msg, &attr) < 0)
return -1;
s->reg = reg;
eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs));
if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
"Selected Registrar");
s->selected_registrar = 0;
} else {
s->selected_registrar = 1;
s->dev_password_id = attr.dev_password_id ?
WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
s->config_methods = attr.sel_reg_config_methods ?
WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
if (attr.authorized_macs) {
int count = attr.authorized_macs_len / ETH_ALEN;
if (count > WPS_MAX_AUTHORIZED_MACS)
count = WPS_MAX_AUTHORIZED_MACS;
os_memcpy(s->authorized_macs, attr.authorized_macs,
count * ETH_ALEN);
} else if (!attr.version2) {
#ifdef CONFIG_WPS2
wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
"AuthorizedMACs for WPS 1.0 ER");
os_memset(s->authorized_macs, 0xff, ETH_ALEN);
#endif /* CONFIG_WPS2 */
}
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
upnp_er_set_selected_timeout, s, NULL);
}
wps_registrar_selected_registrar_changed(reg);
return 0;
}
void upnp_er_remove_notification(struct subscription *s)
{
s->selected_registrar = 0;
eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
if (s->reg)
wps_registrar_selected_registrar_changed(s->reg);
}

View file

@ -0,0 +1,423 @@
/*
* UPnP WPS Device - Event processing
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
#include "includes.h"
#include <assert.h>
#include "common.h"
#include "eloop.h"
#include "uuid.h"
#include "http_client.h"
#include "wps_defs.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
/*
* Event message generation (to subscribers)
*
* We make a separate copy for each message for each subscriber. This memory
* wasted could be limited (adding code complexity) by sharing copies, keeping
* a usage count and freeing when zero.
*
* Sending a message requires using a HTTP over TCP NOTIFY
* (like a PUT) which requires a number of states..
*/
#define MAX_EVENTS_QUEUED 20 /* How far behind queued events */
#define MAX_FAILURES 10 /* Drop subscription after this many failures */
/* How long to wait before sending event */
#define EVENT_DELAY_SECONDS 0
#define EVENT_DELAY_MSEC 0
/*
* Event information that we send to each subscriber is remembered in this
* struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
* over TCP transaction which requires various states.. It may also need to be
* retried at a different address (if more than one is available).
*
* TODO: As an optimization we could share data between subscribers.
*/
struct wps_event_ {
struct dl_list list;
struct subscription *s; /* parent */
unsigned subscriber_sequence; /* which event for this subscription*/
unsigned int retry; /* which retry */
struct subscr_addr *addr; /* address to connect to */
struct wpabuf *data; /* event data to send */
struct http_client *http_event;
};
/* event_clean -- clean sockets etc. of event
* Leaves data, retry count etc. alone.
*/
static void event_clean(struct wps_event_ *e)
{
if (e->s->current_event == e)
e->s->current_event = NULL;
http_client_free(e->http_event);
e->http_event = NULL;
}
/* event_delete -- delete single unqueued event
* (be sure to dequeue first if need be)
*/
static void event_delete(struct wps_event_ *e)
{
wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
event_clean(e);
wpabuf_free(e->data);
os_free(e);
}
/* event_dequeue -- get next event from the queue
* Returns NULL if empty.
*/
static struct wps_event_ *event_dequeue(struct subscription *s)
{
struct wps_event_ *e;
e = dl_list_first(&s->event_queue, struct wps_event_, list);
if (e) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
"subscription %p", e, s);
dl_list_del(&e->list);
}
return e;
}
/* event_delete_all -- delete entire event queue and current event */
void event_delete_all(struct subscription *s)
{
struct wps_event_ *e;
while ((e = event_dequeue(s)) != NULL)
event_delete(e);
if (s->current_event) {
event_delete(s->current_event);
/* will set: s->current_event = NULL; */
}
}
/**
* event_retry - Called when we had a failure delivering event msg
* @e: Event
* @do_next_address: skip address e.g. on connect fail
*/
static void event_retry(struct wps_event_ *e, int do_next_address)
{
struct subscription *s = e->s;
struct upnp_wps_device_sm *sm = s->sm;
wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
e, s);
event_clean(e);
/* will set: s->current_event = NULL; */
if (do_next_address) {
e->retry++;
wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
}
if (e->retry >= dl_list_len(&s->addr_list)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
"for %s", e->addr->domain_and_port);
event_delete(e);
s->last_event_failed = 1;
if (!dl_list_empty(&s->event_queue))
event_send_all_later(s->sm);
return;
}
dl_list_add(&s->event_queue, &e->list);
event_send_all_later(sm);
}
static struct wpabuf * event_build_message(struct wps_event_ *e)
{
struct wpabuf *buf;
char *b;
buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
if (buf == NULL)
return NULL;
wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
"NT: upnp:event\r\n"
"NTS: upnp:propchange\r\n");
wpabuf_put_str(buf, "SID: uuid:");
b = wpabuf_put(buf, 0);
uuid_bin2str(e->s->uuid, b, 80);
wpabuf_put(buf, os_strlen(b));
wpabuf_put_str(buf, "\r\n");
wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
(int) wpabuf_len(e->data));
wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
wpabuf_put_buf(buf, e->data);
return buf;
}
static void event_addr_failure(struct wps_event_ *e)
{
struct subscription *s = e->s;
e->addr->num_failures++;
wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
"(num_failures=%u)",
e, e->addr->domain_and_port, e->addr->num_failures);
if (e->addr->num_failures < MAX_FAILURES) {
/* Try other addresses, if available */
event_retry(e, 1);
return;
}
/*
* If other side doesn't like what we say, forget about them.
* (There is no way to tell other side that we are dropping them...).
*/
wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
"address %s due to errors", s, e->addr->domain_and_port);
dl_list_del(&e->addr->list);
subscr_addr_delete(e->addr);
e->addr = NULL;
if (dl_list_empty(&s->addr_list)) {
/* if we've given up on all addresses */
wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
"with no addresses", s);
dl_list_del(&s->list);
subscription_destroy(s);
return;
}
/* Try other addresses, if available */
event_retry(e, 0);
}
static void event_http_cb(void *ctx, struct http_client *c,
enum http_client_event event)
{
struct wps_event_ *e = ctx;
struct subscription *s = e->s;
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
"event=%d", e, c, event);
switch (event) {
case HTTP_CLIENT_OK:
wpa_printf(MSG_DEBUG,
"WPS UPnP: Got event %p reply OK from %s",
e, e->addr->domain_and_port);
e->addr->num_failures = 0;
s->last_event_failed = 0;
event_delete(e);
/* Schedule sending more if there is more to send */
if (!dl_list_empty(&s->event_queue))
event_send_all_later(s->sm);
break;
case HTTP_CLIENT_FAILED:
wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
event_addr_failure(e);
break;
case HTTP_CLIENT_INVALID_REPLY:
wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
event_addr_failure(e);
break;
case HTTP_CLIENT_TIMEOUT:
wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
event_addr_failure(e);
break;
}
}
/* event_send_start -- prepare to send a event message to subscriber
*
* This gets complicated because:
* -- The message is sent via TCP and we have to keep the stream open
* for 30 seconds to get a response... then close it.
* -- But we might have other event happen in the meantime...
* we have to queue them, if we lose them then the subscriber will
* be forced to unsubscribe and subscribe again.
* -- If multiple URLs are provided then we are supposed to try successive
* ones after 30 second timeout.
* -- The URLs might use domain names instead of dotted decimal addresses,
* and resolution of those may cause unwanted sleeping.
* -- Doing the initial TCP connect can take a while, so we have to come
* back after connection and then send the data.
*
* Returns nonzero on error;
*
* Prerequisite: No current event send (s->current_event == NULL)
* and non-empty queue.
*/
static int event_send_start(struct subscription *s)
{
struct wps_event_ *e;
unsigned int itry;
struct wpabuf *buf;
/*
* Assume we are called ONLY with no current event and ONLY with
* nonempty event queue and ONLY with at least one address to send to.
*/
if (dl_list_empty(&s->addr_list))
return -1;
if (s->current_event)
return -1;
if (dl_list_empty(&s->event_queue))
return -1;
s->current_event = e = event_dequeue(s);
/* Use address according to number of retries */
itry = 0;
dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
if (itry++ == e->retry)
break;
if (itry < e->retry)
return -1;
buf = event_build_message(e);
if (buf == NULL) {
event_retry(e, 0);
return -1;
}
e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
event_http_cb, e);
if (e->http_event == NULL) {
wpabuf_free(buf);
event_retry(e, 0);
return -1;
}
return 0;
}
/* event_send_all_later_handler -- actually send events as needed */
static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
{
struct upnp_wps_device_sm *sm = user_ctx;
struct subscription *s, *tmp;
int nerrors = 0;
sm->event_send_all_queued = 0;
dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
list) {
if (s->current_event == NULL /* not busy */ &&
!dl_list_empty(&s->event_queue) /* more to do */) {
if (event_send_start(s))
nerrors++;
}
}
if (nerrors) {
/* Try again later */
event_send_all_later(sm);
}
}
/* event_send_all_later -- schedule sending events to all subscribers
* that need it.
* This avoids two problems:
* -- After getting a subscription, we should not send the first event
* until after our reply is fully queued to be sent back,
* -- Possible stack depth or infinite recursion issues.
*/
void event_send_all_later(struct upnp_wps_device_sm *sm)
{
/*
* The exact time in the future isn't too important. Waiting a bit
* might let us do several together.
*/
if (sm->event_send_all_queued)
return;
sm->event_send_all_queued = 1;
eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
event_send_all_later_handler, NULL, sm);
}
/* event_send_stop_all -- cleanup */
void event_send_stop_all(struct upnp_wps_device_sm *sm)
{
if (sm->event_send_all_queued)
eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
sm->event_send_all_queued = 0;
}
/**
* event_add - Add a new event to a queue
* @s: Subscription
* @data: Event data (is copied; caller retains ownership)
* @probereq: Whether this is a Probe Request event
* Returns: 0 on success, -1 on error, 1 on max event queue limit reached
*/
int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
{
struct wps_event_ *e;
unsigned int len;
len = dl_list_len(&s->event_queue);
if (len >= MAX_EVENTS_QUEUED) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
"subscriber %p", s);
if (probereq)
return 1;
/* Drop oldest entry to allow EAP event to be stored. */
e = event_dequeue(s);
if (!e)
return 1;
event_delete(e);
}
if (s->last_event_failed && probereq && len > 0) {
/*
* Avoid queuing frames for subscribers that may have left
* without unsubscribing.
*/
wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
"Request frames for subscription %p since last "
"delivery failed", s);
return -1;
}
e = os_zalloc(sizeof(*e));
if (e == NULL)
return -1;
dl_list_init(&e->list);
e->s = s;
e->data = wpabuf_dup(data);
if (e->data == NULL) {
os_free(e);
return -1;
}
e->subscriber_sequence = s->next_subscriber_sequence++;
if (s->next_subscriber_sequence == 0)
s->next_subscriber_sequence++;
wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
"(queue len %u)", e, s, len + 1);
dl_list_add_tail(&s->event_queue, &e->list);
event_send_all_later(s->sm);
return 0;
}

View file

@ -0,0 +1,193 @@
/*
* UPnP for WPS / internal definitions
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
#ifndef WPS_UPNP_I_H
#define WPS_UPNP_I_H
#include "utils/list.h"
#include "http.h"
#define UPNP_MULTICAST_ADDRESS "239.255.255.250" /* for UPnP multicasting */
#define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */
/* min subscribe time per UPnP standard */
#define UPNP_SUBSCRIBE_SEC_MIN 1800
/* subscribe time we use */
#define UPNP_SUBSCRIBE_SEC (UPNP_SUBSCRIBE_SEC_MIN + 1)
/* "filenames" used in URLs that we service via our "web server": */
#define UPNP_WPS_DEVICE_XML_FILE "wps_device.xml"
#define UPNP_WPS_SCPD_XML_FILE "wps_scpd.xml"
#define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control"
#define UPNP_WPS_DEVICE_EVENT_FILE "wps_event"
#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
struct upnp_wps_device_sm;
struct wps_registrar;
enum advertisement_type_enum {
ADVERTISE_UP = 0,
ADVERTISE_DOWN = 1,
MSEARCH_REPLY = 2
};
/*
* Advertisements are broadcast via UDP NOTIFYs, and are also the essence of
* the reply to UDP M-SEARCH requests. This struct handles both cases.
*
* A state machine is needed because a number of variant forms must be sent in
* separate packets and spread out in time to avoid congestion.
*/
struct advertisement_state_machine {
struct dl_list list;
enum advertisement_type_enum type;
int state;
int nerrors;
struct sockaddr_in client; /* for M-SEARCH replies */
};
/*
* An address of a subscriber (who may have multiple addresses). We are
* supposed to send (via TCP) updates to each subscriber, trying each address
* for a subscriber until we find one that seems to work.
*/
struct subscr_addr {
struct dl_list list;
char *domain_and_port; /* domain and port part of url */
char *path; /* "filepath" part of url (from "mem") */
struct sockaddr_in saddr; /* address for doing connect */
unsigned num_failures;
};
/*
* Subscribers to our events are recorded in this struct. This includes a max
* of one outgoing connection (sending an "event message") per subscriber. We
* also have to age out subscribers unless they renew.
*/
struct subscription {
struct dl_list list;
struct upnp_wps_device_sm *sm; /* parent */
time_t timeout_time; /* when to age out the subscription */
unsigned next_subscriber_sequence; /* number our messages */
/*
* This uuid identifies the subscription and is randomly generated by
* us and given to the subscriber when the subscription is accepted;
* and is then included with each event sent to the subscriber.
*/
u8 uuid[UUID_LEN];
/* Linked list of address alternatives (rotate through on failure) */
struct dl_list addr_list;
struct dl_list event_queue; /* Queued event messages. */
struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
*/
int last_event_failed; /* Whether delivery of last event failed */
/* Information from SetSelectedRegistrar action */
u8 selected_registrar;
u16 dev_password_id;
u16 config_methods;
u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
struct wps_registrar *reg;
};
struct upnp_wps_device_interface {
struct dl_list list;
struct upnp_wps_device_ctx *ctx; /* callback table */
struct wps_context *wps;
void *priv;
/* FIX: maintain separate structures for each UPnP peer */
struct upnp_wps_peer peer;
};
/*
* Our instance data corresponding to the AP device. Note that there may be
* multiple wireless interfaces sharing the same UPnP device instance. Each
* such interface is stored in the list of struct upnp_wps_device_interface
* instances.
*
* This is known as an opaque struct declaration to users of the WPS UPnP code.
*/
struct upnp_wps_device_sm {
struct dl_list interfaces; /* struct upnp_wps_device_interface */
char *root_dir;
char *desc_url;
int started; /* nonzero if we are active */
u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
char *ip_addr_text; /* IP address of network i.f. we use */
unsigned ip_addr; /* IP address of network i.f. we use (host order) */
int multicast_sd; /* send multicast messages over this socket */
int ssdp_sd; /* receive discovery UPD packets on socket */
int ssdp_sd_registered; /* nonzero if we must unregister */
unsigned advertise_count; /* how many advertisements done */
struct advertisement_state_machine advertisement;
struct dl_list msearch_replies;
int web_port; /* our port that others get xml files from */
struct http_server *web_srv;
/* Note: subscriptions are kept in expiry order */
struct dl_list subscriptions;
int event_send_all_queued; /* if we are scheduled to send events soon
*/
char *wlanevent; /* the last WLANEvent data */
enum upnp_wps_wlanevent_type wlanevent_type;
os_time_t last_event_sec;
unsigned int num_events_in_sec;
};
/* wps_upnp.c */
void format_date(struct wpabuf *buf);
struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
const char *callback_urls);
struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN]);
void subscription_destroy(struct subscription *s);
struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN]);
void subscr_addr_delete(struct subscr_addr *a);
int send_wpabuf(int fd, struct wpabuf *buf);
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
u8 mac[ETH_ALEN]);
/* wps_upnp_ssdp.c */
void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
int advertisement_state_machine_start(struct upnp_wps_device_sm *sm);
void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
int send_byebye);
void ssdp_listener_stop(struct upnp_wps_device_sm *sm);
int ssdp_listener_start(struct upnp_wps_device_sm *sm);
int ssdp_listener_open(void);
int add_ssdp_network(const char *net_if);
int ssdp_open_multicast_sock(u32 ip_addr);
int ssdp_open_multicast(struct upnp_wps_device_sm *sm);
/* wps_upnp_web.c */
int web_listener_start(struct upnp_wps_device_sm *sm);
void web_listener_stop(struct upnp_wps_device_sm *sm);
/* wps_upnp_event.c */
int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
void event_delete_all(struct subscription *s);
void event_send_all_later(struct upnp_wps_device_sm *sm);
void event_send_stop_all(struct upnp_wps_device_sm *sm);
/* wps_upnp_ap.c */
int upnp_er_set_selected_registrar(struct wps_registrar *reg,
struct subscription *s,
const struct wpabuf *msg);
void upnp_er_remove_notification(struct subscription *s);
#endif /* WPS_UPNP_I_H */

View file

@ -0,0 +1,938 @@
/*
* UPnP SSDP for WPS
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
#include "includes.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <net/route.h>
#include "common.h"
#include "uuid.h"
#include "eloop.h"
#include "wps.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
#define MAX_MSEARCH 20 /* max simultaneous M-SEARCH replies ongoing */
#define SSDP_TARGET "239.0.0.0"
#define SSDP_NETMASK "255.0.0.0"
/* Check tokens for equality, where tokens consist of letters, digits,
* underscore and hyphen, and are matched case insensitive.
*/
static int token_eq(const char *s1, const char *s2)
{
int c1;
int c2;
int end1 = 0;
int end2 = 0;
for (;;) {
c1 = *s1++;
c2 = *s2++;
if (isalpha(c1) && isupper(c1))
c1 = tolower(c1);
if (isalpha(c2) && isupper(c2))
c2 = tolower(c2);
end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
if (end1 || end2 || c1 != c2)
break;
}
return end1 && end2; /* reached end of both words? */
}
/* Return length of token (see above for definition of token) */
static int token_length(const char *s)
{
const char *begin = s;
for (;; s++) {
int c = *s;
int end = !(isalnum(c) || c == '_' || c == '-');
if (end)
break;
}
return s - begin;
}
/* return length of interword separation.
* This accepts only spaces/tabs and thus will not traverse a line
* or buffer ending.
*/
static int word_separation_length(const char *s)
{
const char *begin = s;
for (;; s++) {
int c = *s;
if (c == ' ' || c == '\t')
continue;
break;
}
return s - begin;
}
/* No. of chars through (including) end of line */
static int line_length(const char *l)
{
const char *lp = l;
while (*lp && *lp != '\n')
lp++;
if (*lp == '\n')
lp++;
return lp - l;
}
/* No. of chars excluding trailing whitespace */
static int line_length_stripped(const char *l)
{
const char *lp = l + line_length(l);
while (lp > l && !isgraph(lp[-1]))
lp--;
return lp - l;
}
static int str_starts(const char *str, const char *start)
{
return os_strncmp(str, start, os_strlen(start)) == 0;
}
/***************************************************************************
* Advertisements.
* These are multicast to the world to tell them we are here.
* The individual packets are spread out in time to limit loss,
* and then after a much longer period of time the whole sequence
* is repeated again (for NOTIFYs only).
**************************************************************************/
/**
* next_advertisement - Build next message and advance the state machine
* @a: Advertisement state
* @islast: Buffer for indicating whether this is the last message (= 1)
* Returns: The new message (caller is responsible for freeing this)
*
* Note: next_advertisement is shared code with msearchreply_* functions
*/
static struct wpabuf *
next_advertisement(struct upnp_wps_device_sm *sm,
struct advertisement_state_machine *a, int *islast)
{
struct wpabuf *msg;
char *NTString = "";
char uuid_string[80];
struct upnp_wps_device_interface *iface;
*islast = 0;
iface = dl_list_first(&sm->interfaces,
struct upnp_wps_device_interface, list);
uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
msg = wpabuf_alloc(800); /* more than big enough */
if (msg == NULL)
goto fail;
switch (a->type) {
case ADVERTISE_UP:
case ADVERTISE_DOWN:
NTString = "NT";
wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
wpabuf_printf(msg, "HOST: %s:%d\r\n",
UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
UPNP_CACHE_SEC);
wpabuf_printf(msg, "NTS: %s\r\n",
(a->type == ADVERTISE_UP ?
"ssdp:alive" : "ssdp:byebye"));
break;
case MSEARCH_REPLY:
NTString = "ST";
wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
UPNP_CACHE_SEC);
wpabuf_put_str(msg, "DATE: ");
format_date(msg);
wpabuf_put_str(msg, "\r\n");
wpabuf_put_str(msg, "EXT:\r\n");
break;
}
if (a->type != ADVERTISE_DOWN) {
/* Where others may get our XML files from */
wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
sm->ip_addr_text, sm->web_port,
UPNP_WPS_DEVICE_XML_FILE);
}
/* The SERVER line has three comma-separated fields:
* operating system / version
* upnp version
* software package / version
* However, only the UPnP version is really required, the
* others can be place holders... for security reasons
* it is better to NOT provide extra information.
*/
wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
switch (a->state / UPNP_ADVERTISE_REPEAT) {
case 0:
wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
uuid_string);
break;
case 1:
wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
break;
case 2:
wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
"WFADevice:1\r\n", NTString);
wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
"org:device:WFADevice:1\r\n", uuid_string);
break;
case 3:
wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
"WFAWLANConfig:1\r\n", NTString);
wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
"org:service:WFAWLANConfig:1\r\n", uuid_string);
break;
}
wpabuf_put_str(msg, "\r\n");
if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
*islast = 1;
return msg;
fail:
wpabuf_free(msg);
return NULL;
}
static void advertisement_state_machine_handler(void *eloop_data,
void *user_ctx);
/**
* advertisement_state_machine_stop - Stop SSDP advertisements
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* @send_byebye: Send byebye advertisement messages immediately
*/
void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
int send_byebye)
{
struct advertisement_state_machine *a = &sm->advertisement;
int islast = 0;
struct wpabuf *msg;
struct sockaddr_in dest;
eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
if (!send_byebye || sm->multicast_sd < 0)
return;
a->type = ADVERTISE_DOWN;
a->state = 0;
os_memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
dest.sin_port = htons(UPNP_MULTICAST_PORT);
while (!islast) {
msg = next_advertisement(sm, a, &islast);
if (msg == NULL)
break;
if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg),
0, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto "
"failed: %d (%s)", errno, strerror(errno));
}
wpabuf_free(msg);
a->state++;
}
}
static void advertisement_state_machine_handler(void *eloop_data,
void *user_ctx)
{
struct upnp_wps_device_sm *sm = user_ctx;
struct advertisement_state_machine *a = &sm->advertisement;
struct wpabuf *msg;
int next_timeout_msec = 100;
int next_timeout_sec = 0;
struct sockaddr_in dest;
int islast = 0;
/*
* Each is sent twice (in case lost) w/ 100 msec delay between;
* spec says no more than 3 times.
* One pair for rootdevice, one pair for uuid, and a pair each for
* each of the two urns.
* The entire sequence must be repeated before cache control timeout
* (which is min 1800 seconds),
* recommend random portion of half of the advertised cache control age
* to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
* Delay random interval < 100 msec prior to initial sending.
* TTL of 4
*/
wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
msg = next_advertisement(sm, a, &islast);
if (msg == NULL)
return;
os_memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
dest.sin_port = htons(UPNP_MULTICAST_PORT);
if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
(struct sockaddr *) &dest, sizeof(dest)) == -1) {
wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
"%d (%s)", errno, strerror(errno));
next_timeout_msec = 0;
next_timeout_sec = 10; /* ... later */
} else if (islast) {
a->state = 0; /* wrap around */
if (a->type == ADVERTISE_DOWN) {
wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
a->type = ADVERTISE_UP;
/* do it all over again right away */
} else {
u16 r;
/*
* Start over again after a long timeout
* (see notes above)
*/
next_timeout_msec = 0;
os_get_random((void *) &r, sizeof(r));
next_timeout_sec = UPNP_CACHE_SEC / 4 +
(((UPNP_CACHE_SEC / 4) * r) >> 16);
sm->advertise_count++;
wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
"next in %d sec",
sm->advertise_count, next_timeout_sec);
}
} else {
a->state++;
}
wpabuf_free(msg);
eloop_register_timeout(next_timeout_sec, next_timeout_msec,
advertisement_state_machine_handler, NULL, sm);
}
/**
* advertisement_state_machine_start - Start SSDP advertisements
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* Returns: 0 on success, -1 on failure
*/
int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
{
struct advertisement_state_machine *a = &sm->advertisement;
int next_timeout_msec;
advertisement_state_machine_stop(sm, 0);
/*
* Start out advertising down, this automatically switches
* to advertising up which signals our restart.
*/
a->type = ADVERTISE_DOWN;
a->state = 0;
/* (other fields not used here) */
/* First timeout should be random interval < 100 msec */
next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
return eloop_register_timeout(0, next_timeout_msec,
advertisement_state_machine_handler,
NULL, sm);
}
/***************************************************************************
* M-SEARCH replies
* These are very similar to the multicast advertisements, with some
* small changes in data content; and they are sent (UDP) to a specific
* unicast address instead of multicast.
* They are sent in response to a UDP M-SEARCH packet.
**************************************************************************/
/**
* msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
* @a: Selected advertisement/reply state
*/
void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
{
wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
dl_list_del(&a->list);
os_free(a);
}
static void msearchreply_state_machine_handler(void *eloop_data,
void *user_ctx)
{
struct advertisement_state_machine *a = user_ctx;
struct upnp_wps_device_sm *sm = eloop_data;
struct wpabuf *msg;
int next_timeout_msec = 100;
int next_timeout_sec = 0;
int islast = 0;
/*
* Each response is sent twice (in case lost) w/ 100 msec delay
* between; spec says no more than 3 times.
* One pair for rootdevice, one pair for uuid, and a pair each for
* each of the two urns.
*/
/* TODO: should only send the requested response types */
wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
a->state, inet_ntoa(a->client.sin_addr),
ntohs(a->client.sin_port));
msg = next_advertisement(sm, a, &islast);
if (msg == NULL)
return;
/*
* Send it on the multicast socket to avoid having to set up another
* socket.
*/
if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
(struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
"errno %d (%s) for %s:%d",
errno, strerror(errno),
inet_ntoa(a->client.sin_addr),
ntohs(a->client.sin_port));
/* Ignore error and hope for the best */
}
wpabuf_free(msg);
if (islast) {
wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
msearchreply_state_machine_stop(a);
return;
}
a->state++;
wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
next_timeout_sec, next_timeout_msec);
eloop_register_timeout(next_timeout_sec, next_timeout_msec,
msearchreply_state_machine_handler, sm, a);
}
/**
* msearchreply_state_machine_start - Reply to M-SEARCH discovery request
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* @client: Client address
* @mx: Maximum delay in seconds
*
* Use TTL of 4 (this was done when socket set up).
* A response should be given in randomized portion of min(MX,120) seconds
*
* UPnP-arch-DeviceArchitecture, 1.2.3:
* To be found, a device must send a UDP response to the source IP address and
* port that sent the request to the multicast channel. Devices respond if the
* ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
* followed by a UUID that exactly matches one advertised by the device.
*/
static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
struct sockaddr_in *client,
int mx)
{
struct advertisement_state_machine *a;
int next_timeout_sec;
int next_timeout_msec;
int replies;
replies = dl_list_len(&sm->msearch_replies);
wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
"outstanding)", replies);
if (replies >= MAX_MSEARCH) {
wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
"M-SEARCH replies");
return;
}
a = os_zalloc(sizeof(*a));
if (a == NULL)
return;
a->type = MSEARCH_REPLY;
a->state = 0;
os_memcpy(&a->client, client, sizeof(*client));
/* Wait time depending on MX value */
next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
next_timeout_sec = next_timeout_msec / 1000;
next_timeout_msec = next_timeout_msec % 1000;
if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
msearchreply_state_machine_handler, sm,
a)) {
/* No way to recover (from malloc failure) */
goto fail;
}
/* Remember for future cleanup */
dl_list_add(&sm->msearch_replies, &a->list);
return;
fail:
wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
os_free(a);
}
/**
* ssdp_parse_msearch - Process a received M-SEARCH
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* @client: Client address
* @data: NULL terminated M-SEARCH message
*
* Given that we have received a header w/ M-SEARCH, act upon it
*
* Format of M-SEARCH (case insensitive!):
*
* First line must be:
* M-SEARCH * HTTP/1.1
* Other lines in arbitrary order:
* HOST:239.255.255.250:1900
* ST:<varies -- must match>
* MAN:"ssdp:discover"
* MX:<varies>
*
* It should be noted that when Microsoft Vista is still learning its IP
* address, it sends out host lines like: HOST:[FF02::C]:1900
*/
static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
struct sockaddr_in *client, const char *data)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
const char *start = data;
#endif /* CONFIG_NO_STDOUT_DEBUG */
const char *end;
int got_host = 0;
int got_st = 0, st_match = 0;
int got_man = 0;
int got_mx = 0;
int mx = 0;
/*
* Skip first line M-SEARCH * HTTP/1.1
* (perhaps we should check remainder of the line for syntax)
*/
data += line_length(data);
/* Parse remaining lines */
for (; *data != '\0'; data += line_length(data)) {
end = data + line_length_stripped(data);
if (token_eq(data, "host")) {
/* The host line indicates who the packet
* is addressed to... but do we really care?
* Note that Microsoft sometimes does funny
* stuff with the HOST: line.
*/
#if 0 /* could be */
data += token_length(data);
data += word_separation_length(data);
if (*data != ':')
goto bad;
data++;
data += word_separation_length(data);
/* UPNP_MULTICAST_ADDRESS */
if (!str_starts(data, "239.255.255.250"))
goto bad;
data += os_strlen("239.255.255.250");
if (*data == ':') {
if (!str_starts(data, ":1900"))
goto bad;
}
#endif /* could be */
got_host = 1;
continue;
} else if (token_eq(data, "st")) {
/* There are a number of forms; we look
* for one that matches our case.
*/
got_st = 1;
data += token_length(data);
data += word_separation_length(data);
if (*data != ':')
continue;
data++;
data += word_separation_length(data);
if (str_starts(data, "ssdp:all")) {
st_match = 1;
continue;
}
if (str_starts(data, "upnp:rootdevice")) {
st_match = 1;
continue;
}
if (str_starts(data, "uuid:")) {
char uuid_string[80];
struct upnp_wps_device_interface *iface;
iface = dl_list_first(
&sm->interfaces,
struct upnp_wps_device_interface,
list);
data += os_strlen("uuid:");
uuid_bin2str(iface->wps->uuid, uuid_string,
sizeof(uuid_string));
if (str_starts(data, uuid_string))
st_match = 1;
continue;
}
#if 0
/* FIX: should we really reply to IGD string? */
if (str_starts(data, "urn:schemas-upnp-org:device:"
"InternetGatewayDevice:1")) {
st_match = 1;
continue;
}
#endif
if (str_starts(data, "urn:schemas-wifialliance-org:"
"service:WFAWLANConfig:1")) {
st_match = 1;
continue;
}
if (str_starts(data, "urn:schemas-wifialliance-org:"
"device:WFADevice:1")) {
st_match = 1;
continue;
}
continue;
} else if (token_eq(data, "man")) {
data += token_length(data);
data += word_separation_length(data);
if (*data != ':')
continue;
data++;
data += word_separation_length(data);
if (!str_starts(data, "\"ssdp:discover\"")) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
"M-SEARCH man-field");
goto bad;
}
got_man = 1;
continue;
} else if (token_eq(data, "mx")) {
data += token_length(data);
data += word_separation_length(data);
if (*data != ':')
continue;
data++;
data += word_separation_length(data);
mx = atol(data);
got_mx = 1;
continue;
}
/* ignore anything else */
}
if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
"%d mx=%d", got_host, got_st, got_man, got_mx, mx);
goto bad;
}
if (!st_match) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
"match)");
return;
}
if (mx > 120)
mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
msearchreply_state_machine_start(sm, client, mx);
return;
bad:
wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
}
/* Listening for (UDP) discovery (M-SEARCH) packets */
/**
* ssdp_listener_stop - Stop SSDP listered
* @sm: WPS UPnP state machine from upnp_wps_device_init()
*
* This function stops the SSDP listener that was started by calling
* ssdp_listener_start().
*/
void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
{
if (sm->ssdp_sd_registered) {
eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
sm->ssdp_sd_registered = 0;
}
if (sm->ssdp_sd != -1) {
close(sm->ssdp_sd);
sm->ssdp_sd = -1;
}
eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
ELOOP_ALL_CTX);
}
static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
{
struct upnp_wps_device_sm *sm = sock_ctx;
struct sockaddr_in addr; /* client address */
socklen_t addr_len;
int nread;
char buf[MULTICAST_MAX_READ], *pos;
addr_len = sizeof(addr);
nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
(struct sockaddr *) &addr, &addr_len);
if (nread <= 0)
return;
buf[nread] = '\0'; /* need null termination for algorithm */
if (str_starts(buf, "NOTIFY ")) {
/*
* Silently ignore NOTIFYs to avoid filling debug log with
* unwanted messages.
*/
return;
}
pos = os_strchr(buf, '\n');
if (pos)
*pos = '\0';
wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
"%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
if (pos)
*pos = '\n';
/* Parse first line */
if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
!isgraph(buf[strlen("M-SEARCH")])) {
ssdp_parse_msearch(sm, &addr, buf);
return;
}
/* Ignore anything else */
}
int ssdp_listener_open(void)
{
struct sockaddr_in addr;
struct ip_mreq mcast_addr;
int on = 1;
/* per UPnP spec, keep IP packet time to live (TTL) small */
unsigned char ttl = 4;
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0)
goto fail;
if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
goto fail;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
goto fail;
os_memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(UPNP_MULTICAST_PORT);
if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
goto fail;
os_memset(&mcast_addr, 0, sizeof(mcast_addr));
mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *) &mcast_addr, sizeof(mcast_addr)))
goto fail;
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
&ttl, sizeof(ttl)))
goto fail;
return sd;
fail:
if (sd >= 0)
close(sd);
return -1;
}
/**
* ssdp_listener_start - Set up for receiving discovery (UDP) packets
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* Returns: 0 on success, -1 on failure
*
* The SSDP listener is stopped by calling ssdp_listener_stop().
*/
int ssdp_listener_start(struct upnp_wps_device_sm *sm)
{
sm->ssdp_sd = ssdp_listener_open();
if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
ssdp_listener_handler, NULL, sm))
goto fail;
sm->ssdp_sd_registered = 1;
return 0;
fail:
/* Error */
wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
ssdp_listener_stop(sm);
return -1;
}
/**
* add_ssdp_network - Add routing entry for SSDP
* @net_if: Selected network interface name
* Returns: 0 on success, -1 on failure
*
* This function assures that the multicast address will be properly
* handled by Linux networking code (by a modification to routing tables).
* This must be done per network interface. It really only needs to be done
* once after booting up, but it does not hurt to call this more frequently
* "to be safe".
*/
int add_ssdp_network(const char *net_if)
{
#ifdef __linux__
int ret = -1;
int sock = -1;
struct rtentry rt;
struct sockaddr_in *sin;
if (!net_if)
goto fail;
os_memset(&rt, 0, sizeof(rt));
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
goto fail;
rt.rt_dev = (char *) net_if;
sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in);
sin->sin_family = AF_INET;
sin->sin_port = 0;
sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
rt.rt_flags = RTF_UP;
if (ioctl(sock, SIOCADDRT, &rt) < 0) {
if (errno == EPERM) {
wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
"permissions to add routing table entry");
/* Continue to allow testing as non-root */
} else if (errno != EEXIST) {
wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
"%d (%s)", errno, strerror(errno));
goto fail;
}
}
ret = 0;
fail:
if (sock >= 0)
close(sock);
return ret;
#else /* __linux__ */
return 0;
#endif /* __linux__ */
}
int ssdp_open_multicast_sock(u32 ip_addr)
{
int sd;
/* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
* time to live (TTL) small */
unsigned char ttl = 4;
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0)
return -1;
#if 0 /* maybe ok if we sometimes block on writes */
if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
return -1;
#endif
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
&ip_addr, sizeof(ip_addr))) {
wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
"%d (%s)", ip_addr, errno, strerror(errno));
return -1;
}
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
&ttl, sizeof(ttl))) {
wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
"%d (%s)", errno, strerror(errno));
return -1;
}
#if 0 /* not needed, because we don't receive using multicast_sd */
{
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
mreq.imr_interface.s_addr = ip_addr;
wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
"0x%x",
mreq.imr_multiaddr.s_addr,
mreq.imr_interface.s_addr);
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq))) {
wpa_printf(MSG_ERROR,
"WPS UPnP: setsockopt "
"IP_ADD_MEMBERSHIP errno %d (%s)",
errno, strerror(errno));
return -1;
}
}
#endif /* not needed */
/*
* TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
* which aids debugging I suppose but isn't really necessary?
*/
return sd;
}
/**
* ssdp_open_multicast - Open socket for sending multicast SSDP messages
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* Returns: 0 on success, -1 on failure
*/
int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
{
sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr);
if (sm->multicast_sd < 0)
return -1;
return 0;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff