From b08b70faf873455ff14dcd633a7c9eb860ba4b28 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 10 Mar 2001 11:38:27 +0000 Subject: started support for unicode on the wire in smbd. Using a very similar method to what was used in the client I now have session setup and tconx working. Currently this is enabled with SMBD_USE_UNICODE environment variable. Once the code is complete this will become a smb.conf option. (This used to be commit 7684c1e67294266d018c6f0cab58f1a9d797174f) --- source3/smbd/negprot.c | 6 ++ source3/smbd/reply.c | 69 +++++++++--------- source3/smbd/srvstr.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+), 36 deletions(-) create mode 100644 source3/smbd/srvstr.c (limited to 'source3/smbd') diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c index 74d8eb3983..c2026f46f9 100644 --- a/source3/smbd/negprot.c +++ b/source3/smbd/negprot.c @@ -201,6 +201,12 @@ static int reply_nt1(char *outbuf) capabilities |= CAP_RAW_MODE; } + + /* until the unicode conversion is complete have it disabled by default */ + if (getenv("SMBD_USE_UNICODE")) { + capabilities |= CAP_UNICODE; + } + #ifdef WITH_MSDFS if(lp_host_msdfs()) capabilities |= CAP_DFS; diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 4e87782a48..4f98c264b8 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -273,12 +273,11 @@ int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt pstring user; pstring password; pstring devicename; - BOOL doencrypt = SMBENCRYPT(); int ecode = -1; uint16 vuid = SVAL(inbuf,smb_uid); int passlen = SVAL(inbuf,smb_vwv3); - char *path; - char *p; + pstring path; + char *p, *q; START_PROFILE(SMBtconX); *service = *user = *password = *devicename = 0; @@ -294,7 +293,8 @@ int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt memcpy(password,smb_buf(inbuf),passlen); password[passlen]=0; - path = smb_buf(inbuf) + passlen; + p = smb_buf(inbuf) + passlen; + p += srvstr_pull(inbuf, path, p, sizeof(path), -1, STR_TERMINATE|STR_CONVERT); if (passlen != 24) { if (strequal(password," ")) @@ -302,27 +302,20 @@ int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt passlen = strlen(password); } - p = strchr(path+2,'\\'); - if (!p) { + q = strchr(path+2,'\\'); + if (!q) { END_PROFILE(SMBtconX); return(ERROR(ERRDOS,ERRnosuchshare)); } - fstrcpy(service,p+1); - p = strchr(service,'%'); - if (p) { - *p++ = 0; - fstrcpy(user,p); + fstrcpy(service,q+1); + q = strchr(service,'%'); + if (q) { + *q++ = 0; + fstrcpy(user,q); } - StrnCpy(devicename,path + strlen(path) + 1,6); - DEBUG(4,("Got device type %s\n",devicename)); + p += srvstr_pull(inbuf, devicename, p, sizeof(devicename), 6, STR_CONVERT); - /* - * Ensure the user and password names are in UNIX codepage format. - */ - - dos_to_unix(user,True); - if (!doencrypt) - dos_to_unix(password,True); + DEBUG(4,("Got device type %s\n",devicename)); /* * Pass the user through the NT -> unix user mapping @@ -349,13 +342,13 @@ int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt } else { char *fsname = lp_fstype(SNUM(conn)); - set_message(outbuf,3,3,True); + set_message(outbuf,3,0,True); p = smb_buf(outbuf); - pstrcpy(p,devicename); p = skip_string(p,1); /* device name */ - pstrcpy(p,fsname); p = skip_string(p,1); /* filesystem type e.g NTFS */ + p += srvstr_push(inbuf, outbuf, p, devicename, -1, STR_CONVERT|STR_TERMINATE); + p += srvstr_push(inbuf, outbuf, p, fsname, -1, STR_CONVERT|STR_TERMINATE); - set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False); + set_message_end(outbuf,p); /* what does setting this bit do? It is set by NT4 and may affect the ability to autorun mounted cdroms */ @@ -703,10 +696,12 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,int BOOL valid_lm_password = False; pstring user; pstring orig_user; + fstring domain; + fstring native_os; + fstring native_lanman; BOOL guest=False; static BOOL done_sesssetup = False; BOOL doencrypt = SMBENCRYPT(); - char *domain = ""; START_PROFILE(SMBsesssetupX); *smb_apasswd = 0; @@ -835,17 +830,19 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,int } p += passlen1 + passlen2; - fstrcpy(user,p); - p = skip_string(p,1); + p += srvstr_pull(inbuf, user, p, sizeof(user), -1, STR_CONVERT|STR_TERMINATE); /* * Incoming user and domain are in DOS codepage format. Convert * to UNIX. */ - dos_to_unix(user,True); - domain = p; - dos_to_unix(domain, True); + p += srvstr_pull(inbuf, domain, p, sizeof(domain), + -1, STR_CONVERT|STR_TERMINATE); + p += srvstr_pull(inbuf, native_os, p, sizeof(native_os), + -1, STR_CONVERT|STR_TERMINATE); + p += srvstr_pull(inbuf, native_lanman, p, sizeof(native_lanman), + -1, STR_CONVERT|STR_TERMINATE); DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n", - domain,skip_string(p,1),skip_string(p,2))); + domain,native_os,native_lanman)); } DEBUG(3,("sesssetupX:name=[%s]\n",user)); @@ -1027,12 +1024,12 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,int set_message(outbuf,3,0,True); } else { char *p; - set_message(outbuf,3,3,True); + set_message(outbuf,3,0,True); p = smb_buf(outbuf); - pstrcpy(p,"Unix"); p = skip_string(p,1); - pstrcpy(p,"Samba "); pstrcat(p,VERSION); p = skip_string(p,1); - pstrcpy(p,global_myworkgroup); unix_to_dos(p, True); p = skip_string(p,1); - set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False); + p += srvstr_push(inbuf, outbuf, p, "Unix", -1, STR_TERMINATE|STR_CONVERT); + p += srvstr_push(inbuf, outbuf, p, "Samba", -1, STR_TERMINATE|STR_CONVERT); + p += srvstr_push(inbuf, outbuf, p, global_myworkgroup, -1, STR_TERMINATE|STR_CONVERT); + set_message_end(outbuf,p); /* perhaps grab OS version here?? */ } diff --git a/source3/smbd/srvstr.c b/source3/smbd/srvstr.c new file mode 100644 index 0000000000..e420b8fa99 --- /dev/null +++ b/source3/smbd/srvstr.c @@ -0,0 +1,186 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + server specific string routines + Copyright (C) Andrew Tridgell 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +#define UNICODE_FLAG() (SVAL(inbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) + +/**************************************************************************** +copy a string from a char* src to a unicode or ascii +dos code page destination choosing unicode or ascii based on the +FLAGS2_UNICODE_STRINGS bit in inbuf +return the number of bytes occupied by the string in the destination +flags can have: + STR_TERMINATE means include the null termination + STR_CONVERT means convert from unix to dos codepage + STR_UPPER means uppercase in the destination + STR_ASCII use ascii even with unicode servers +dest_len is the maximum length allowed in the destination. If dest_len +is -1 then no maxiumum is used +****************************************************************************/ +int srvstr_push(void *inbuf, void *outbuf, void *dest, const char *src, int dest_len, int flags) +{ + int len=0; + + /* treat a pstring as "unlimited" length */ + if (dest_len == -1) { + dest_len = sizeof(pstring); + } + + if (!(flags & STR_ASCII) && srvstr_align(inbuf, PTR_DIFF(dest, outbuf))) { + *(char *)dest = 0; + dest++; + dest_len--; + len++; + } + + if ((flags & STR_ASCII) || !UNICODE_FLAG()) { + /* the client doesn't want unicode */ + safe_strcpy(dest, src, dest_len); + len = strlen(dest); + if (flags & STR_TERMINATE) len++; + if (flags & STR_CONVERT) unix_to_dos(dest,True); + if (flags & STR_UPPER) strupper(dest); + return len; + } + + /* the server likes unicode. give it the works */ + if (flags & STR_CONVERT) { + dos_PutUniCode(dest, src, dest_len, flags & STR_TERMINATE); + } else { + ascii_to_unistr(dest, src, dest_len); + } + if (flags & STR_UPPER) { + strupper_w(dest); + } + len += strlen(src)*2; + if (flags & STR_TERMINATE) len += 2; + return len; +} + + +/**************************************************************************** +return the length that a string would occupy when copied with srvstr_push() + STR_TERMINATE means include the null termination + STR_CONVERT means convert from unix to dos codepage + STR_UPPER means uppercase in the destination +note that dest is only used for alignment purposes. No data is written. +****************************************************************************/ +int srvstr_push_size(void *inbuf, void *outbuf, + const void *dest, const char *src, int dest_len, int flags) +{ + int len = strlen(src); + if (flags & STR_TERMINATE) len++; + if (!(flags & STR_ASCII) && UNICODE_FLAG()) len *= 2; + + if (!(flags & STR_ASCII) && dest && srvstr_align(inbuf, PTR_DIFF(outbuf, dest))) { + len++; + } + + return len; +} + +/**************************************************************************** +copy a string from a unicode or ascii source (depending on flg2) +to a char* destination +flags can have: + STR_CONVERT means convert from dos to unix codepage + STR_TERMINATE means the string in src is null terminated + STR_UNICODE means to force as unicode +if STR_TERMINATE is set then src_len is ignored +src_len is the length of the source area in bytes +return the number of bytes occupied by the string in src +****************************************************************************/ +int srvstr_pull(void *inbuf, char *dest, const void *src, int dest_len, int src_len, int flags) +{ + int len; + + if (dest_len == -1) { + dest_len = sizeof(pstring); + } + + if (srvstr_align(inbuf, PTR_DIFF(src, inbuf))) { + src++; + if (src_len > 0) src_len--; + } + + if (!(flags & STR_UNICODE) && !UNICODE_FLAG()) { + /* the server doesn't want unicode */ + if (flags & STR_TERMINATE) { + safe_strcpy(dest, src, dest_len); + len = strlen(src)+1; + } else { + if (src_len > dest_len) src_len = dest_len; + len = src_len; + memcpy(dest, src, len); + dest[len] = 0; + } + if (flags & STR_CONVERT) dos_to_unix(dest,True); + return len; + } + + if (flags & STR_TERMINATE) { + unistr_to_ascii(dest, src, dest_len); + len = strlen(dest)*2 + 2; + } else { + int i, c; + if (dest_len*2 < src_len) src_len = 2*dest_len; + for (i=0; i < src_len; i += 2) { + c = SVAL(src, i); + *dest++ = c; + } + *dest++ = 0; + len = src_len; + } + if (flags & STR_CONVERT) dos_to_unix(dest,True); + return len; +} + +/**************************************************************************** +return the length that a string would occupy (not including the null) +when copied with srvstr_pull() +if src_len is -1 then assume the source is null terminated +****************************************************************************/ +int srvstr_pull_size(void *inbuf, const void *src, int src_len) +{ + if (srvstr_align(inbuf, PTR_DIFF(src, inbuf))) { + src++; + if (src_len > 0) src_len--; + } + + if (!UNICODE_FLAG()) { + return strlen(src); + } + return strlen_w(src); +} + +/**************************************************************************** +return an alignment of either 0 or 1 +if unicode is not negotiated then return 0 +otherwise return 1 if offset is off +****************************************************************************/ +int srvstr_align(void *inbuf, int offset) +{ + if (!UNICODE_FLAG()) return 0; + return offset & 1; +} -- cgit