From b6401220c0bf7482ad5b204d08ba831a7cf9eabb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 26 Apr 2011 14:41:31 -0700 Subject: [PATCH] Additional sysappend strings from DMI; pre-bake the http cookies - Add additional sysappend strings from DMI; we may want to add even more but let's think about it first. - Pre-generate http cookies. - Add a "sendcookies" command to mask out some of the information. Signed-off-by: H. Peter Anvin --- com32/include/syslinux/sysappend.h | 6 +- com32/lib/Makefile | 2 +- core/dmi.c | 256 +++++++++++++++++++++++++++++++++++++ core/extern.inc | 5 +- core/fs/pxe/http.c | 61 +++++---- core/fs/pxe/pxe.c | 111 ++-------------- core/fs/pxe/pxe.h | 1 + core/include/core.h | 1 + core/init.inc | 6 + core/keywords | 1 + core/keywords.inc | 3 + core/parseconfig.inc | 9 ++ core/sysappend.c | 67 +++++++++- 13 files changed, 401 insertions(+), 128 deletions(-) create mode 100644 core/dmi.c diff --git a/com32/include/syslinux/sysappend.h b/com32/include/syslinux/sysappend.h index dcc42dc..b767bf2 100644 --- a/com32/include/syslinux/sysappend.h +++ b/com32/include/syslinux/sysappend.h @@ -37,7 +37,11 @@ enum syslinux_sysappend { SYSAPPEND_IP, /* PXELINUX: ip= address */ SYSAPPEND_BOOTIF, /* PXELINUX: BOOTIF= address */ - SYSAPPEND_UUID, /* System UUID from PXE or DMI */ + SYSAPPEND_SYSUUID, /* System UUID from PXE or DMI */ + SYSAPPEND_SYSVENDOR, /* System vendor from DMI */ + SYSAPPEND_SYSPRODUCT, /* System product from DMI */ + SYSAPPEND_SYSVERSION, /* System version from DMI */ + SYSAPPEND_SYSSERIAL, /* System serial from DMI */ SYSAPPEND_MAX /* Total number of strings */ }; diff --git a/com32/lib/Makefile b/com32/lib/Makefile index 105c2bd..20002ac 100644 --- a/com32/lib/Makefile +++ b/com32/lib/Makefile @@ -134,7 +134,7 @@ LIBCOREOBJS = \ strlen.o stpcpy.o strcpy.o strcmp.o strlcpy.o strlcat.o \ strchr.o strncmp.o strncpy.o \ \ - snprintf.o sprintf.o vsnprintf.o \ + asprintf.o snprintf.o sprintf.o vsnprintf.o \ \ dprintf.o vdprintf.o \ \ diff --git a/core/dmi.c b/core/dmi.c new file mode 100644 index 0000000..674d27c --- /dev/null +++ b/core/dmi.c @@ -0,0 +1,256 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2011 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * Search DMI information for specific data or strings + */ + +#include +#include +#include +#include "core.h" + +struct dmi_table { + uint8_t type; + uint8_t length; + uint16_t handle; +}; + +struct dmi_header { + char signature[5]; + uint8_t csum; + uint16_t tbllen; + uint32_t tbladdr; + uint16_t nstruc; + uint8_t revision; + uint8_t reserved; +}; + +struct smbios_header { + char signature[4]; + uint8_t csum; + uint8_t len; + uint8_t major; + uint8_t minor; + uint16_t maxsize; + uint8_t revision; + uint8_t fmt[5]; + + struct dmi_header dmi; +}; + +static const struct dmi_header *dmi; + +static uint8_t checksum(const void *buf, size_t len) +{ + const uint8_t *p = buf; + uint8_t csum = 0; + + while (len--) + csum += *p++; + + return csum; +} + +static bool is_old_dmi(size_t dptr) +{ + const struct dmi_header *dmi = (void *)dptr; + + return !memcmp(dmi->signature, "_DMI_", 5) && + !checksum(dmi, 0x0f); + return false; +} + +static bool is_smbios(size_t dptr) +{ + const struct smbios_header *smb = (void *)dptr; + + return !memcmp(smb->signature, "_SM_", 4) && + !checksum(smb, smb->len) && + is_old_dmi(dptr+16); +} + +/* + * Find the root structure + */ +static void dmi_find_header(void) +{ + size_t dptr; + + /* Search for _SM_ or _DMI_ structure */ + for (dptr = 0xf0000 ; dptr < 0x100000 ; dptr += 16) { + if (is_smbios(dptr)) { + dmi = (const struct dmi_header *)(dptr + 16); + break; + } else if (is_old_dmi(dptr)) { + dmi = (const struct dmi_header *)dptr; + break; + } + } +} + +/* + * Return a specific data element in a specific table, and verify + * that it is within the bounds of the table. + */ +static const void *dmi_find_data(uint8_t type, uint8_t base, uint8_t length) +{ + const struct dmi_table *table; + size_t offset, end; + unsigned int tblcount; + + if (!dmi) + return NULL; + + if (base < 2) + return NULL; + + end = base+length; + + offset = 0; + tblcount = dmi->nstruc; + + while (offset+2 < dmi->tbllen && tblcount) { + table = (const struct dmi_table *)(dmi->tbladdr + offset); + + if (table->length < sizeof *table) + break; /* Invalid length */ + + offset += table->length; + + if (table->type == type && end <= table->length) + return (const char *)table + base; + + /* Search for a double NUL terminating the string table */ + while (offset+2 < dmi->tbllen && + *(const uint16_t *)(dmi->tbladdr + offset) != 0) + offset++; + + offset += 2; + tblcount--; + } + + return NULL; +} + +/* + * Return a specific string in a specific table. + */ +static const char *dmi_find_string(uint8_t type, uint8_t base) +{ + const struct dmi_table *table; + size_t offset; + unsigned int tblcount; + + if (!dmi) + return NULL; + + if (base < 2) + return NULL; + + offset = 0; + tblcount = dmi->nstruc; + + while (offset+2 < dmi->tbllen && tblcount) { + table = (const struct dmi_table *)(dmi->tbladdr + offset); + + if (table->length < sizeof *table) + break; /* Invalid length */ + + offset += table->length; + + if (table->type == type && base < table->length) { + uint8_t index = ((const uint8_t *)table)[base]; + const char *p = (const char *)table + table->length; + char c; + + if (!index) + return NULL; /* String not present */ + + while (--index) { + if (!*p) + return NULL; + + do { + c = *p; + if (++offset >= dmi->tbllen) + return NULL; + p++; + } while (c); + } + + return p; + } + + /* Search for a double NUL terminating the string table */ + while (offset+2 < dmi->tbllen && + *(const uint16_t *)(dmi->tbladdr + offset) != 0) + offset++; + + offset += 2; + } + + return NULL; +} + +struct sysappend_dmi_strings { + const char *prefix; + enum syslinux_sysappend sa; + uint8_t index; + uint8_t offset; +}; + +static const struct sysappend_dmi_strings dmi_strings[] = { + { "SYSVENDOR=", SYSAPPEND_SYSVENDOR, 1, 0x04 }, + { "SYSPRODUCT=", SYSAPPEND_SYSPRODUCT, 1, 0x05 }, + { "SYSVERSION=", SYSAPPEND_SYSVERSION, 1, 0x06 }, + { "SYSSERIAL=", SYSAPPEND_SYSSERIAL, 1, 0x07 }, + { NULL, 0, 0, 0 } +}; + +void dmi_init(void) +{ + const struct sysappend_dmi_strings *ds; + + dmi_find_header(); + if (!dmi) + return; + + sysappend_set_uuid(dmi_find_data(1, 0x08, 16)); + + for (ds = dmi_strings; ds->prefix; ds++) { + const char *str = dmi_find_string(ds->index, ds->offset); + + if (sysappend_strings[ds->sa]) { + free((char *)sysappend_strings[ds->sa]); + sysappend_strings[ds->sa] = NULL; + } + if (str) + asprintf((char **)&sysappend_strings[ds->sa], + "%s%s", ds->prefix, str); + } +} diff --git a/core/extern.inc b/core/extern.inc index 433d55e..3858f70 100644 --- a/core/extern.inc +++ b/core/extern.inc @@ -30,9 +30,12 @@ ; sysappend.c extern do_sysappend, print_sysappend + ; dmi.c + extern dmi_init + %if IS_PXELINUX ; pxe.c - extern unload_pxe, reset_pxe + extern unload_pxe, reset_pxe, http_bake_cookies %endif %endif ; EXTERN_INC diff --git a/core/fs/pxe/http.c b/core/fs/pxe/http.c index ae5e010..d98cc4a 100644 --- a/core/fs/pxe/http.c +++ b/core/fs/pxe/http.c @@ -46,8 +46,10 @@ static bool append_ch(char *str, size_t size, size_t *pos, int ch) return success; } -static size_t cookie_len; -static char *cookie_buf; +static size_t cookie_len, header_len; +static char *cookie_buf, *header_buf; + +extern uint32_t SendCookies; static size_t http_do_bake_cookies(char *q) { @@ -59,9 +61,10 @@ static size_t http_do_bake_cookies(char *q) char c; size_t qlen = q ? -1UL : 0; bool first = true; + uint32_t mask = SendCookies; for (i = 0; i < SYSAPPEND_MAX; i++) { - if ((p = sysappend_strings[i])) { + if ((mask & 1) && (p = sysappend_strings[i])) { len = snprintf(q, qlen, "%s_Syslinux_", first ? "Cookie: " : ""); if (q) q += len; @@ -96,6 +99,7 @@ static size_t http_do_bake_cookies(char *q) *q++ = ';'; n++; } + mask >>= 1; } if (!first) { if (q) { @@ -117,8 +121,20 @@ void http_bake_cookies(void) cookie_len = http_do_bake_cookies(NULL); cookie_buf = malloc(cookie_len+1); - if (!cookie_buf) + if (!cookie_buf) { + cookie_len = 0; return; + } + + if (header_buf) + free(header_buf); + + header_len = cookie_len + 6*FILENAME_MAX + 256; + header_buf = malloc(header_len); + if (!header_buf) { + header_len = 0; + return; /* Uh-oh... */ + } http_do_bake_cookies(cookie_buf); } @@ -126,8 +142,7 @@ void http_bake_cookies(void) void http_open(struct url_info *url, struct inode *inode, const char **redir) { struct pxe_pvt_inode *socket = PVT(inode); - char header_buf[4096]; - int header_len; + int header_bytes; const char *next; char field_name[20]; char field_value[1024]; @@ -151,9 +166,8 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) int status; int pos; - /* XXX: make this an external call at the appropriate time instead */ - if (!cookie_buf) - http_bake_cookies(); + if (!header_buf) + return; /* http is broken... */ socket->fill_buffer = tcp_fill_buffer; socket->close = tcp_close_file; @@ -178,26 +192,25 @@ void http_open(struct url_info *url, struct inode *inode, const char **redir) } strcpy(header_buf, "GET /"); - header_len = 5; - header_len += url_escape_unsafe(header_buf+5, url->path, + header_bytes = 5; + header_bytes += url_escape_unsafe(header_buf+5, url->path, sizeof header_buf - 5); - if (header_len > sizeof header_buf) + if (header_bytes > header_len) goto fail; /* Buffer overflow */ - header_len += snprintf(header_buf + header_len, - sizeof header_buf - header_len, - " HTTP/1.0\r\n" - "Host: %s\r\n" - "User-Agent: PXELINUX/%s\r\n" - "Connection: close\r\n" - "%s" - "\r\n", - url->host, VERSION_STR, - cookie_buf ? cookie_buf : ""); - if (header_len > sizeof header_buf) + header_bytes += snprintf(header_buf + header_bytes, + header_len - header_bytes, + " HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: PXELINUX/" VERSION_STR "\r\n" + "Connection: close\r\n" + "%s" + "\r\n", + url->host, cookie_buf ? cookie_buf : ""); + if (header_bytes > sizeof header_buf) goto fail; /* Buffer overflow */ err = netconn_write(socket->conn, header_buf, - header_len, NETCONN_NOCOPY); + header_bytes, NETCONN_NOCOPY); if (err) { printf("netconn_write error %d\n", err); goto fail; diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c index c1b8476..4bf8d00 100644 --- a/core/fs/pxe/pxe.c +++ b/core/fs/pxe/pxe.c @@ -77,50 +77,6 @@ static void pxe_close_file(struct file *file) free_socket(inode); } -/** - * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal - * - * @param: dst, output buffer - * @param: src, input buffer - * @param: count, number of bytes - * - */ -static void lchexbytes(char *dst, const void *src, int count) -{ - uint8_t half; - uint8_t c; - const uint8_t *s = src; - - for(; count > 0; count--) { - c = *s++; - half = ((c >> 4) & 0x0f) + '0'; - *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half; - - half = (c & 0x0f) + '0'; - *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half; - } -} - -/* - * just like the lchexbytes, except to upper-case - * - */ -static void uchexbytes(char *dst, const void *src, int count) -{ - uint8_t half; - uint8_t c; - const uint8_t *s = src; - - for(; count > 0; count--) { - c = *s++; - half = ((c >> 4) & 0x0f) + '0'; - *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half; - - half = (c & 0x0f) + '0'; - *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half; - } -} - /* * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good. * We used to refuse class E, but class E addresses are likely to become @@ -149,27 +105,11 @@ bool ip_ok(uint32_t ip) */ static int gendotquad(char *dst, uint32_t ip) { - int part; - int i = 0, j; - char temp[4]; - char *p = dst; - - for (; i < 4; i++) { - j = 0; - part = ip & 0xff; - do { - temp[j++] = (part % 10) + '0'; - }while(part /= 10); - for (; j > 0; j--) - *p++ = temp[j-1]; - *p++ = '.'; - - ip >>= 8; - } - /* drop the last dot '.' and zero-terminate string*/ - *(--p) = 0; - - return p - dst; + return sprintf(dst, "%u.%u.%u.%u", + ((const uint8_t *)&ip)[0], + ((const uint8_t *)&ip)[1], + ((const uint8_t *)&ip)[2], + ((const uint8_t *)&ip)[3]); } /* @@ -544,8 +484,8 @@ static int pxe_load_config(void) config_file = stpcpy(ConfigName, cfgprefix); /* Try loading by UUID */ - if (sysappend_strings[SYSAPPEND_UUID]) { - strcpy(config_file, sysappend_strings[SYSAPPEND_UUID]+8); + if (sysappend_strings[SYSAPPEND_SYSUUID]) { + strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8); if (try_load(ConfigName)) return 0; } @@ -556,7 +496,7 @@ static int pxe_load_config(void) return 0; /* Nope, try hexadecimal IP prefixes... */ - uchexbytes(config_file, (uint8_t *)&IPInfo.myip, 4); + sprintf(config_file, "%08X", ntohl(IPInfo.myip)); last = &config_file[8]; while (tries) { *last = '\0'; /* Zero-terminate string */ @@ -592,36 +532,6 @@ static void make_bootif_string(void) sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str; } -/* - * Generate the SYSUUID string, if we have one... - */ -static void make_sysuuid_string(void) -{ - static char sysuuid_str[8+32+5]; - static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0}; - const uint8_t *src = uuid; - const uint8_t *uuid_ptr = uuid_dashes; - char *dst; - - /* Try loading by UUID */ - if (have_uuid) { - dst = stpcpy(sysuuid_str, "SYSUUID="); - - while (*uuid_ptr) { - int len = *uuid_ptr; - - lchexbytes(dst, src, len); - dst += len * 2; - src += len; - uuid_ptr++; - *dst++ = '-'; - } - /* Remove last dash and zero-terminate */ - *--dst = '\0'; - - sysappend_strings[SYSAPPEND_UUID] = sysuuid_str; - } -} /* * Generate an ip=::: @@ -937,8 +847,11 @@ static void network_init(void) printf("\n"); make_bootif_string(); - make_sysuuid_string(); + /* If DMI and DHCP disagree, which one should we set? */ + if (have_uuid) + sysappend_set_uuid(uuid); ip_init(); + http_bake_cookies(); print_sysappend(); /* diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h index d3e7499..0200c1e 100644 --- a/core/fs/pxe/pxe.h +++ b/core/fs/pxe/pxe.h @@ -227,6 +227,7 @@ void gpxe_open(struct inode *inode, const char *url); /* http.c */ void http_open(struct url_info *url, struct inode *inode, const char **redir); +void http_bake_cookies(void); /* ftp.c */ void ftp_open(struct url_info *url, struct inode *inode, const char **redir); diff --git a/core/include/core.h b/core/include/core.h index 4420fec..965b990 100644 --- a/core/include/core.h +++ b/core/include/core.h @@ -48,6 +48,7 @@ extern void mem_init(void); extern void print_sysappend(void); extern const char *sysappend_strings[SYSAPPEND_MAX]; extern uint32_t SysAppends; +extern void sysappend_set_uuid(const uint8_t *uuid); void __cdecl core_intcall(uint8_t, const com32sys_t *, com32sys_t *); void __cdecl core_farcall(uint32_t, const com32sys_t *, com32sys_t *); diff --git a/core/init.inc b/core/init.inc index 97889ca..75dc783 100644 --- a/core/init.inc +++ b/core/init.inc @@ -88,6 +88,12 @@ check_escapes: enough_ram: skip_checks: +; +; Scan the DMI tables for interesting information +; + pm_call dmi_init + + section .data16 err_noram db 'It appears your computer has less than ' .size db '000' diff --git a/core/keywords b/core/keywords index 80972ad..8af0095 100644 --- a/core/keywords +++ b/core/keywords @@ -35,6 +35,7 @@ noescape nocomplete nohalt sysappend +sendcookies f0 f1 f2 diff --git a/core/keywords.inc b/core/keywords.inc index 6441740..d91ca4f 100644 --- a/core/keywords.inc +++ b/core/keywords.inc @@ -95,5 +95,8 @@ keywd_table: keyword ipappend, pc_sysappend keyword sysappend, pc_sysappend keyword localboot, pc_localboot +%if IS_PXELINUX + keyword sendcookies, pc_sendcookies +%endif keywd_count equ ($-keywd_table)/keywd_size diff --git a/core/parseconfig.inc b/core/parseconfig.inc index 51a6d8d..a846b5a 100644 --- a/core/parseconfig.inc +++ b/core/parseconfig.inc @@ -81,6 +81,15 @@ pc_sysappend: call getint ret ; +; "sendcookies" command +; +%if IS_PXELINUX +pc_sendcookies: call pc_setint32 + pm_call http_bake_cookies + ret +%endif + +; ; "localboot" command ; pc_localboot: call getint diff --git a/core/sysappend.c b/core/sysappend.c index ac35b22..e84e4b2 100644 --- a/core/sysappend.c +++ b/core/sysappend.c @@ -12,6 +12,7 @@ #include #include +#include #include "core.h" /* @@ -23,6 +24,31 @@ extern uint32_t SysAppends; /* Configuration variable */ const char *sysappend_strings[SYSAPPEND_MAX]; /* + * Copy a string, converting whitespace characters to underscores + * and compacting them. + */ +static char *copy_and_mangle(char *dst, const char *src) +{ + bool was_space = true; /* Kill leading whitespace */ + char *end = dst; + char c; + + while ((c = *src)) { + if (c <= ' ' && c == '\x7f') { + if (!was_space) + *dst++ = '_'; + was_space = true; + } else { + *dst++ = c; + end = dst; + was_space = false; + } + } + *end = '\0'; + return end; +} + +/* * Handle sysappend strings for the old real-mode command line generator... * this code should be replaced when all that code is coverted to C. * @@ -36,8 +62,8 @@ void do_sysappend(com32sys_t *regs) uint32_t mask = SysAppends; for (i = 0; i < SYSAPPEND_MAX; i++) { - if (mask & 1) { - q = stpcpy(q, sysappend_strings[i]); + if ((mask & 1) && sysappend_strings[i]) { + q = copy_and_mangle(q, sysappend_strings[i]); *q++ = ' '; } mask >>= 1; @@ -48,6 +74,43 @@ void do_sysappend(com32sys_t *regs) } /* + * Generate the SYSUUID= sysappend string + */ +static bool is_valid_uuid(const uint8_t *uuid) +{ + /* Assume the uuid is valid if it has a type that is not 0 or 15 */ + return (uuid[6] >= 0x10 && uuid[6] < 0xf0); +} + +void sysappend_set_uuid(const uint8_t *src) +{ + static char sysuuid_str[8+32+5] = "SYSUUID="; + static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0}; + const uint8_t *uuid_ptr = uuid_dashes; + char *dst; + + if (!src || !is_valid_uuid(src)) + return; + + dst = sysuuid_str+8; + + while (*uuid_ptr) { + int len = *uuid_ptr; + + while (len) { + dst += sprintf(dst, "%02x", *src++); + len--; + } + uuid_ptr++; + *dst++ = '-'; + } + /* Remove last dash and zero-terminate */ + *--dst = '\0'; + + sysappend_strings[SYSAPPEND_SYSUUID] = sysuuid_str; +} + +/* * Print the sysappend strings, in order */ void print_sysappend(void) -- 2.7.4