The branch, v3-4-test has been updated via 8ac6085... Fix bug #7122 - Reading a large browselist fails (server returns invalid values in subsequent SMBtrans replies) via 026f058... Fix off-by-one error in working out the limit of the NetServerEnum comment. via 8142883... s3:smbd: use StrCaseCmp() instead of strcasecmp via 4e419df... s3:smbd: Fix really ugly bool vs. int bug!!! via 86eae5b... s3:libsmb: fix NetServerEnum3 rap calls. via f37f187... s3:smbd: implement api_RNetServerEnum3 via 2614ed6... util: added binsearch.h for binary array searches via f8f6bef... s3:smbd: add/improve some DEBUG messages in api_RNetServerEnum2() via 0b6d850... s3:smbd: rename api_RNetServerEnum => api_RNetServerEnum2 from 832fed7... Missed one check on the memcpy for bug #7063.
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v3-4-test - Log ----------------------------------------------------------------- commit 8ac6085a2c7382e544888e632ff62c30f7e7a884 Author: Jeremy Allison <j...@samba.org> Date: Tue Feb 9 15:14:38 2010 -0800 Fix bug #7122 - Reading a large browselist fails (server returns invalid values in subsequent SMBtrans replies) There are two problems: 1). The server is off-by-one in the end of buffer space test. 2). The server returns 0 in the totaldata (smb_vwv1) and totalparams (smb_vwv0) fields in the second and subsequent SMBtrans replies. This patch fixes both. Jeremy. (cherry picked from commit 8ddc977c1421a47bedba8d5494f7ae67692b772a) Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 026f05839b6dbdeb5be3953930a28f7650c1e1da Author: Jeremy Allison <j...@samba.org> Date: Tue Feb 9 12:17:08 2010 -0800 Fix off-by-one error in working out the limit of the NetServerEnum comment. Jeremy. (cherry picked from commit 9ad6f432f3f5844b4b419e7cbaf3c3e70b052d29) Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 8142883b40819b5cb92ea671bb6c89bff68d3680 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Feb 9 18:58:36 2010 +0100 s3:smbd: use StrCaseCmp() instead of strcasecmp metze (cherry picked from commit bc8242a08e1bb9489cc8171b1ec02bd2518b1857) commit 4e419df9154c329b3376ab00d6bb55093fbfe71a Author: Stefan Metzmacher <me...@samba.org> Date: Tue Feb 9 18:54:41 2010 +0100 s3:smbd: Fix really ugly bool vs. int bug!!! A comparison function for qsort needs to return an 'int'! Otherwise you'll get random results depending on the compiler and the architecture... metze (cherry picked from commit 1686a5e7e7eb1b411b003cbbde5c0d28741c6d02) commit 86eae5b4862735309313e1800be44dab2641b393 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Feb 8 18:38:03 2010 +0100 s3:libsmb: fix NetServerEnum3 rap calls. metze (cherry picked from commit 9b5198dd443a00fdad4faa1f9cdabedd81012d93) commit f37f187070934e1046ce05d298d92ede7e6f7030 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Feb 8 19:07:45 2010 +0100 s3:smbd: implement api_RNetServerEnum3 This is needed to support large browse lists. metze (cherry picked from commit 30eec0656c926d3d85a438dc28f17649b53318f8) commit 2614ed62c8ca281d95151913ed591a86409e0566 Author: Andrew Tridgell <tri...@samba.org> Date: Thu Dec 10 14:35:24 2009 +1100 util: added binsearch.h for binary array searches This was moved from the schema_query code. It will now be used in more than one place, so best to make it a library macro. I think there are quite a few places that could benefit from this. (cherry picked from commit 71943e8858943718affb6a3c0ded2127f07057f0) Signed-off-by: Stefan Metzmacher <me...@samba.org> (cherry picked from commit 448b8f35d7a7cff73d35304673302178f593c9d0) Signed-off-by: Stefan Metzmacher <me...@samba.org> commit f8f6beff57fd58b69648633f5b1c15289015f96b Author: Stefan Metzmacher <me...@samba.org> Date: Mon Feb 8 18:45:18 2010 +0100 s3:smbd: add/improve some DEBUG messages in api_RNetServerEnum2() metze (cherry picked from commit 495ac4616654c9e62e14031b7439aff21e42ec91) commit 0b6d850a553c0a558d579ab5e46f49794a015e34 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Feb 5 16:55:15 2010 +0100 s3:smbd: rename api_RNetServerEnum => api_RNetServerEnum2 metze (cherry picked from commit dc58672c6588a1715698721153b35ed2d594bc67) ----------------------------------------------------------------------- Summary of changes: lib/util/binsearch.h | 68 ++++++++++++++ source3/libsmb/clirap.c | 26 ++++- source3/smbd/ipc.c | 3 + source3/smbd/lanman.c | 234 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 314 insertions(+), 17 deletions(-) create mode 100644 lib/util/binsearch.h Changeset truncated at 500 lines: diff --git a/lib/util/binsearch.h b/lib/util/binsearch.h new file mode 100644 index 0000000..ac83990 --- /dev/null +++ b/lib/util/binsearch.h @@ -0,0 +1,68 @@ +/* + Unix SMB/CIFS implementation. + + a generic binary search macro + + Copyright (C) Andrew Tridgell 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _BINSEARCH_H +#define _BINSEARCH_H + +/* a binary array search, where the array is an array of pointers to structures, + and we want to find a match for 'target' on 'field' in those structures. + + Inputs: + array: base pointer to an array of structures + arrray_size: number of elements in the array + field: the name of the field in the structure we are keying off + target: the field value we are looking for + comparison_fn: the comparison function + result: where the result of the search is put + + if the element is found, then 'result' is set to point to the found array element. If not, + then 'result' is set to NULL. + + The array is assumed to be sorted by the same comparison_fn as the + search (with, for example, qsort) + */ +#define BINARY_ARRAY_SEARCH_P(array, array_size, field, target, comparison_fn, result) do { \ + int32_t _b, _e; \ + (result) = NULL; \ + if (array_size) { for (_b = 0, _e = (array_size)-1; _b <= _e; ) { \ + int32_t _i = (_b+_e)/2; \ + int _r = comparison_fn(target, array[_i]->field); \ + if (_r == 0) { (result) = array[_i]; break; } \ + if (_r < 0) _e = _i - 1; else _b = _i + 1; \ + }} } while (0) + +/* + like BINARY_ARRAY_SEARCH_P, but assumes that the array is an array + of structures, rather than pointers to structures + + result points to the found structure, or NULL + */ +#define BINARY_ARRAY_SEARCH(array, array_size, field, target, comparison_fn, result) do { \ + int32_t _b, _e; \ + (result) = NULL; \ + if (array_size) { for (_b = 0, _e = (array_size)-1; _b <= _e; ) { \ + int32_t _i = (_b+_e)/2; \ + int _r = comparison_fn(target, array[_i].field); \ + if (_r == 0) { (result) = &array[_i]; break; } \ + if (_r < 0) _e = _i - 1; else _b = _i + 1; \ + }} } while (0) + +#endif diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 9705cac..2abb550 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -247,11 +247,9 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, p = param; SIVAL(p,0,func); /* api number */ p += 2; - /* Next time through we need to use the continue api */ - func = RAP_NetServerEnum3; - if (last_entry) { - strlcpy(p,"WrLehDOz", sizeof(param)-PTR_DIFF(p,param)); + if (func == RAP_NetServerEnum3) { + strlcpy(p,"WrLehDzz", sizeof(param)-PTR_DIFF(p,param)); } else { strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param)); } @@ -270,7 +268,7 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, * to continue from. */ len = push_ascii(p, - last_entry ? last_entry : workgroup, + workgroup, sizeof(param) - PTR_DIFF(p,param) - 1, STR_TERMINATE|STR_UPPER); @@ -280,6 +278,22 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, } p += len; + if (func == RAP_NetServerEnum3) { + len = push_ascii(p, + last_entry ? last_entry : "", + sizeof(param) - PTR_DIFF(p,param) - 1, + STR_TERMINATE); + + if (len == (size_t)-1) { + SAFE_FREE(last_entry); + return false; + } + p += len; + } + + /* Next time through we need to use the continue api */ + func = RAP_NetServerEnum3; + if (!cli_api(cli, param, PTR_DIFF(p,param), 8, /* params, length, max */ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ @@ -352,7 +366,7 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, comment_offset = (IVAL(p,22) & 0xFFFF)-converter; cmnt = comment_offset?(rdata+comment_offset):""; - if (comment_offset < 0 || comment_offset > (int)rdrcnt) { + if (comment_offset < 0 || comment_offset >= (int)rdrcnt) { TALLOC_FREE(frame); continue; } diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index 5fd756e..2cf0038 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -170,6 +170,9 @@ void send_trans_reply(connection_struct *conn, rparam, tot_param_sent, this_lparam, rdata, tot_data_sent, this_ldata); + SSVAL(req->outbuf,smb_vwv0,lparam); + SSVAL(req->outbuf,smb_vwv1,ldata); + SSVAL(req->outbuf,smb_vwv3,this_lparam); SSVAL(req->outbuf,smb_vwv4, smb_offset(smb_buf(req->outbuf)+1,req->outbuf)); diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index b15e685..40b6aca 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -26,6 +26,7 @@ */ #include "includes.h" +#include "../lib/util/binsearch.h" #ifdef CHECK_TYPES #undef CHECK_TYPES @@ -1352,9 +1353,9 @@ static int fill_srv_info(struct srv_info_struct *service, } -static bool srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2) +static int srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2) { - return(strcmp(s1->name,s2->name)); + return StrCaseCmp(s1->name,s2->name); } /**************************************************************************** @@ -1362,7 +1363,7 @@ static bool srv_comp(struct srv_info_struct *s1,struct srv_info_struct *s2) extracted from lists saved by nmbd on the local host. ****************************************************************************/ -static bool api_RNetServerEnum(connection_struct *conn, uint16 vuid, +static bool api_RNetServerEnum2(connection_struct *conn, uint16 vuid, char *param, int tpscnt, char *data, int tdscnt, int mdrcnt, int mprcnt, char **rdata, @@ -1431,6 +1432,8 @@ static bool api_RNetServerEnum(connection_struct *conn, uint16 vuid, fstrcpy(domain, lp_workgroup()); } + DEBUG(4, ("domain [%s]\n", domain)); + if (lp_browse_list()) { total = get_server_info(servertype,&servers,domain); } @@ -1453,10 +1456,10 @@ static bool api_RNetServerEnum(connection_struct *conn, uint16 vuid, } lastname = s->name; data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0); - DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n", - s->name, s->type, s->comment, s->domain)); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); - if (data_len <= buf_len) { + if (data_len < buf_len) { counted++; fixed_len += f_len; string_len += s_len; @@ -1489,8 +1492,8 @@ static bool api_RNetServerEnum(connection_struct *conn, uint16 vuid, } lastname = s->name; fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata); - DEBUG(4,("fill_srv_info %20s %8x %25s %15s\n", - s->name, s->type, s->comment, s->domain)); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); count2--; } } @@ -1507,12 +1510,220 @@ static bool api_RNetServerEnum(connection_struct *conn, uint16 vuid, SAFE_FREE(servers); - DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n", + DEBUG(3,("NetServerEnum2 domain = %s uLevel=%d counted=%d total=%d\n", domain,uLevel,counted,counted+missed)); return True; } +static int srv_name_match(const char *n1, const char *n2) +{ + /* + * [MS-RAP] footnote <88> for Section 3.2.5.15 says: + * + * In Windows, FirstNameToReturn need not be an exact match: + * the server will return a list of servers that exist on + * the network greater than or equal to the FirstNameToReturn. + */ + int ret = StrCaseCmp(n1, n2); + + if (ret <= 0) { + return 0; + } + + return ret; +} + +static bool api_RNetServerEnum3(connection_struct *conn, uint16 vuid, + char *param, int tpscnt, + char *data, int tdscnt, + int mdrcnt, int mprcnt, char **rdata, + char **rparam, int *rdata_len, int *rparam_len) +{ + char *str1 = get_safe_str_ptr(param, tpscnt, param, 2); + char *str2 = skip_string(param,tpscnt,str1); + char *p = skip_string(param,tpscnt,str2); + int uLevel = get_safe_SVAL(param, tpscnt, p, 0, -1); + int buf_len = get_safe_SVAL(param,tpscnt, p, 2, 0); + uint32 servertype = get_safe_IVAL(param,tpscnt,p,4, 0); + char *p2; + int data_len, fixed_len, string_len; + int f_len = 0, s_len = 0; + struct srv_info_struct *servers=NULL; + int counted=0,first=0,total=0; + int i,missed; + fstring domain; + fstring first_name; + bool domain_request; + bool local_request; + + if (!str1 || !str2 || !p) { + return False; + } + + /* If someone sets all the bits they don't really mean to set + DOMAIN_ENUM and LOCAL_LIST_ONLY, they just want all the + known servers. */ + + if (servertype == SV_TYPE_ALL) { + servertype &= ~(SV_TYPE_DOMAIN_ENUM|SV_TYPE_LOCAL_LIST_ONLY); + } + + /* If someone sets SV_TYPE_LOCAL_LIST_ONLY but hasn't set + any other bit (they may just set this bit on its own) they + want all the locally seen servers. However this bit can be + set on its own so set the requested servers to be + ALL - DOMAIN_ENUM. */ + + if ((servertype & SV_TYPE_LOCAL_LIST_ONLY) && !(servertype & SV_TYPE_DOMAIN_ENUM)) { + servertype = SV_TYPE_ALL & ~(SV_TYPE_DOMAIN_ENUM); + } + + domain_request = ((servertype & SV_TYPE_DOMAIN_ENUM) != 0); + local_request = ((servertype & SV_TYPE_LOCAL_LIST_ONLY) != 0); + + p += 8; + + if (strcmp(str1, "WrLehDzz") != 0) { + return false; + } + if (!check_server_info(uLevel,str2)) { + return False; + } + + DEBUG(4, ("server request level: %s %8x ", str2, servertype)); + DEBUG(4, ("domains_req:%s ", BOOLSTR(domain_request))); + DEBUG(4, ("local_only:%s\n", BOOLSTR(local_request))); + + if (skip_string(param,tpscnt,p) == NULL) { + return False; + } + pull_ascii_fstring(domain, p); + if (domain[0] == '\0') { + fstrcpy(domain, lp_workgroup()); + } + p = skip_string(param,tpscnt,p); + if (skip_string(param,tpscnt,p) == NULL) { + return False; + } + pull_ascii_fstring(first_name, p); + + DEBUG(4, ("domain: '%s' first_name: '%s'\n", + domain, first_name)); + + if (lp_browse_list()) { + total = get_server_info(servertype,&servers,domain); + } + + data_len = fixed_len = string_len = 0; + missed = 0; + + if (total > 0) { + qsort(servers,total,sizeof(servers[0]),QSORT_CAST srv_comp); + } + + if (first_name[0] != '\0') { + struct srv_info_struct *first_server = NULL; + + BINARY_ARRAY_SEARCH(servers, total, name, first_name, + srv_name_match, first_server); + if (first_server) { + first = PTR_DIFF(first_server, servers) / sizeof(*servers); + /* + * The binary search may not find the exact match + * so we need to search backward to find the first match + * + * This implements the strange matching windows + * implements. (see the comment in srv_name_match(). + */ + for (;first > 0;) { + int ret; + ret = StrCaseCmp(first_name, + servers[first-1].name); + if (ret > 0) { + break; + } + first--; + } + } else { + /* we should return no entries */ + first = total; + } + } + + { + char *lastname=NULL; + + for (i=first;i<total;i++) { + struct srv_info_struct *s = &servers[i]; + + if (lastname && strequal(lastname,s->name)) { + continue; + } + lastname = s->name; + data_len += fill_srv_info(s,uLevel,0,&f_len,0,&s_len,0); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); + + if (data_len < buf_len) { + counted++; + fixed_len += f_len; + string_len += s_len; + } else { + missed++; + } + } + } + + *rdata_len = fixed_len + string_len; + *rdata = smb_realloc_limit(*rdata,*rdata_len); + if (!*rdata) { + return False; + } + + p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */ + p = *rdata; + f_len = fixed_len; + s_len = string_len; + + { + char *lastname=NULL; + int count2 = counted; + + for (i = first; i < total && count2;i++) { + struct srv_info_struct *s = &servers[i]; + + if (lastname && strequal(lastname,s->name)) { + continue; + } + lastname = s->name; + fill_srv_info(s,uLevel,&p,&f_len,&p2,&s_len,*rdata); + DEBUG(4,("fill_srv_info[%d] %20s %8x %25s %15s\n", + i, s->name, s->type, s->comment, s->domain)); + count2--; + } + } + + *rparam_len = 8; + *rparam = smb_realloc_limit(*rparam,*rparam_len); + if (!*rparam) { + return False; + } + SSVAL(*rparam,0,(missed == 0 ? NERR_Success : ERRmoredata)); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,counted); + SSVAL(*rparam,6,counted+missed); + + DEBUG(3,("NetServerEnum3 domain = %s uLevel=%d first=%d[%s => %s] counted=%d total=%d\n", + domain,uLevel,first,first_name, + first < total ? servers[first].name : "", + counted,counted+missed)); + + SAFE_FREE(servers); + + return True; +} + /**************************************************************************** command 0x34 - suspected of being a "Lookup Names" stub api ****************************************************************************/ @@ -1819,7 +2030,7 @@ static bool api_RNetShareEnum( connection_struct *conn, uint16 vuid, if( lp_browseable( i ) && lp_snum_ok( i ) && (strlen(servicename_dos) < 13)) { total++; data_len += fill_share_info(conn,i,uLevel,0,&f_len,0,&s_len,0); - if (data_len <= buf_len) { + if (data_len < buf_len) { counted++; fixed_len += f_len; string_len += s_len; @@ -4628,7 +4839,8 @@ static const struct { {"WPrintDestGetInfo", RAP_WPrintDestGetInfo, api_WPrintDestGetInfo}, {"NetRemoteTOD", RAP_NetRemoteTOD, api_NetRemoteTOD}, {"WPrintQueuePurge", RAP_WPrintQPurge, api_WPrintQueueCtrl}, - {"NetServerEnum", RAP_NetServerEnum2, api_RNetServerEnum}, /* anon OK */ + {"NetServerEnum2", RAP_NetServerEnum2, api_RNetServerEnum2}, /* anon OK */ + {"NetServerEnum3", RAP_NetServerEnum3, api_RNetServerEnum3}, /* anon OK */ {"WAccessGetUserPerms",RAP_WAccessGetUserPerms,api_WAccessGetUserPerms}, {"SetUserPassword", RAP_WUserPasswordSet2, api_SetUserPassword}, {"WWkstaUserLogon", RAP_WWkstaUserLogon, api_WWkstaUserLogon}, -- Samba Shared Repository