The Samba-Bugzilla – Attachment 15985 Details for
Bug 14385
Make spoofing of the workstaiton name more difficult to improve userWorkstations attribute access verification
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch for ntlmssp_server.c
ntlmssp_server.c.patch_draft_specialip_and_reversedns (text/plain), 14.22 KB, created by
Sysadmin HTL-Leonding
on 2020-05-16 22:08:19 UTC
(
hide
)
Description:
Patch for ntlmssp_server.c
Filename:
MIME Type:
Creator:
Sysadmin HTL-Leonding
Created:
2020-05-16 22:08:19 UTC
Size:
14.22 KB
patch
obsolete
>--- orig/samba-4.11.6/auth/ntlmssp/ntlmssp_server.c 2020-01-08 10:24:52.000000000 +0000 >+++ samba-4.11.6+dfsg/auth/ntlmssp/ntlmssp_server.c 2020-05-16 20:56:33.723451300 +0000 >@@ -35,11 +35,17 @@ > #include "param/param.h" > #include "param/loadparm.h" > #include "libcli/security/session.h" >+#include "lib/tsocket/tsocket.h" >+ > > #include "lib/crypto/gnutls_helpers.h" > #include <gnutls/gnutls.h> > #include <gnutls/crypto.h> > >+#include <sys/socket.h> >+#include <arpa/inet.h> >+#include <netdb.h> >+ > #undef DBGC_CLASS > #define DBGC_CLASS DBGC_AUTH > >@@ -405,6 +411,115 @@ > return tevent_req_post(req, ev); > } > >+int getippart(char *begin, char *end) >+{ >+ >+ char *p=begin; >+ int i=0; >+ if (end-begin>3)return -1; >+ if (end==begin) return -1; >+ DEBUG(1, ("IP part validator: %s %s\n",begin,end)); >+ while (p!=end) >+ { >+ if (isdigit(*p)) >+ { >+ i=i*10+(*p-'0'); >+ } >+ else return -1; >+ p++; >+ } >+ DEBUG(1, ("IP part validator: %i\n",i)); >+ if (i>255)return -1; >+ else return i; >+} >+ >+bool isencodedprivateip(char *str, const char delimiter) >+{ >+ bool invalidip=true; >+ char *begin,*end; >+ int part1=0,part2=0,part3=0,part4=0; >+ begin = str; >+ end = strchr(str,delimiter); >+ DEBUG(1, ("IP string is %s and delimiter is %c\n",str,delimiter)); >+ if (end!=NULL) >+ { >+ if((part1=getippart(begin,end))==-1) >+ { >+ DEBUG(1, ("First part is not a valid IPv4 address\n")); >+ } >+ else >+ { >+ begin=end+1; >+ end = strchr(begin,delimiter); >+ if (end!=NULL) >+ { >+ if((part2=getippart(begin,end))==-1) >+ { >+ DEBUG(1, ("Second part is not a valid IPv4 address\n")); >+ } >+ else >+ { >+ begin=end+1; >+ end = strchr(begin,delimiter); >+ if (end!=NULL) >+ { >+ if((part3=getippart(begin,end))==-1) >+ { >+ DEBUG(1, ("Third part is not a valid IPv4 address\n")); >+ } >+ else >+ { >+ begin=end+1; >+ end = strchr(begin,delimiter); >+ if (end==NULL) >+ { >+ char *realend=begin; >+ //while (*realend!='\0')realend++; >+ >+ //things like 10_20_30_40L should be allowed >+ while (isdigit(*realend))realend++; >+ >+ if((part4=getippart(begin,realend))==-1) >+ { >+ DEBUG(1, ("Forth part is not a valid IPv4 address\n")); >+ } >+ else >+ { >+ DEBUG(1, ("IP parts are %i %i %i %i\n",part1, part2, part3, part4)); >+ >+ //private IPv4 ranges are - RFC1928 >+ //10.x.x.x >+ //172.16.x.x-172.31.x.x >+ //192.168.x.x >+ if (part1==10){ >+ DEBUG(1, ("10.x.x.x range\n")); >+ invalidip=false; >+ } else >+ if ((part1==172)&&(part2>=16)&&(part2<=31)) >+ { >+ DEBUG(1, ("172.16.x.x-172.31.x.x range\n")); >+ invalidip=false; >+ } else >+ if ((part1==192)&&(part2==168)) >+ { >+ DEBUG(1, ("192.168.x.x\n")); >+ invalidip=false; >+ } >+ //MAYBE 127.x.x.x should be added? >+ } >+ } >+ else DEBUG(1,("Too much delimiters found\n")); >+ } >+ } >+ else DEBUG(1,("Third delimiter not found\n")); >+ } >+ } >+ else DEBUG(1,("Second delimiter not found\n")); >+ } >+ }else DEBUG(1,("First delimiter not found\n")); >+ return !invalidip; >+} >+ > /** > * Next state function for the Authenticate packet > * >@@ -520,6 +635,274 @@ > } > } > >+ //This could would have to be included/copied to all other authentication types >+ >+ //AD and Samba know a userWorkstations attribute >+ //This attribute would be expected by admins to prevent users from logging in from untrusted workstations >+ //or anyone/anything else knowing a username/password combination from logging in >+ //without having access to a specific workstation. >+ >+ //It could be seen as being like a 2FA being the workstation the 2nd factor >+ >+ //In the case of a file server it has to pass on the workstation name of the calling station >+ //to grant users access (adding the file server to the list would allow access from anywhere) >+ //Thus AD seems to depend on the file server to do verification whether the caller >+ //is trustworthy and to do some kind of identity verification. >+ >+ //Having untrusted people knowing username/password combinations isn't good, >+ //but there are special cases where you control access to a set of specific workstations >+ //and want to restrict access to file shares to come from those controlled computers. >+ >+ //This could be done by some smb.conf include magic depending on the caller's IP and the username >+ //but when there is an userWorkstations attribute admins depend on, it should be doing the same >+ //out of the box. >+ >+ //The real solution would be having the workstation to authenticate itself in a secure way. >+ //For domain joined computers there should be some way as there is a computer workstation >+ //object in AD - but this might need changes in the underlying protocol. >+ >+ >+ //Solution 1: >+ //Special case workstation name verification >+ //Imagine the admin used the workstation name to represent the IPv4 address >+ //replacing dots by underscores to prevent confusion with DNS names (as the userWorkstations >+ //attribute could also store DNS names (at least when accessed by scripts and not the GUI)) >+ //which would cause workstation name verification to match only the first part of the address. >+ >+ //While userWorkstation attribute matching works to prevent workstations with other names to access >+ //we must prevent other IPv4 addresses to claim that they would be the allowed workstation. >+ >+ //Better way would be to look up the IPv4 address somewhere in the AD attributes of the workstation >+ //object and to compare it (which wouldn't help if it could be claimed by any other fake client). >+ >+ //Some kind of snake oil anyway, but possible safer than the previous checks >+ //as long as the admin can prevent others from claiming the IPv4 address (i.e. by implementing >+ //VLAN segmentation) and the same snake oil like smb.conf include magic >+ >+ //This implementation always enforces the workstation name to match the IPv4 address >+ //if the workstation name could be an encoded IP address >+ //For dual boot support it allows configurable strings to be added to the workstation name. >+ >+ >+ struct tsocket_address *remoteaddr=gensec_get_remote_address(gensec_security); >+ if (tsocket_address_is_inet(remoteaddr,"ipv4")) >+ { >+ bool isspecialip = false; >+ bool specialipisvalid = false; >+ if (lpcfg_workstation_name_ip_encoding(gensec_security->settings->lp_ctx)){ >+ /* max IPv4 is 255.255.255.255 thus 15 chars + 1 null byte */ >+ DEBUG(1, ("lpcfg_workstation_name_ip_encoding is enabled, do checks\n")); >+ TALLOC_CTX *mytmp_ctx=talloc_new(NULL); >+ char *myip; >+ char *delimiter; >+ const char *olddelimiter; >+ const char **allowedsuffixes = lpcfg_workstation_name_ip_encoding_suffix(gensec_security->settings->lp_ctx); >+ myip = tsocket_address_inet_addr_string(remoteaddr,mytmp_ctx); >+ for (int k=0; k<3; k++) >+ { >+ delimiter = strchr(myip,'.'); >+ *delimiter = '_'; >+ } >+ DEBUG(1, ("ntlmssp_server_auth comparision: %s %s\n",ntlmssp_state->client.netbios_name,myip)); >+ if (isencodedprivateip(ntlmssp_state->client.netbios_name,'_')) >+ { >+ DEBUG(1, ("We have a specially encoded private IPv4 address as workstation name\n")); >+ isspecialip = true; >+ if (strcmp(ntlmssp_state->client.netbios_name,myip)==0) >+ specialipisvalid=true; >+ else >+ { >+ int len=strlen(myip); >+ if(strncmp(myip,ntlmssp_state->client.netbios_name,len)==0) >+ { >+ if (allowedsuffixes != NULL) >+ { >+ const char **asp = allowedsuffixes; >+ char *nbname=ntlmssp_state->client.netbios_name; >+ nbname+=len; >+ DEBUG(1, ("Allowed suffixes exist\n")); >+ while (*asp != NULL) >+ { >+ DEBUG(1, ("Compare suffixes\n")); >+ DEBUG(1, ("Compare suffix %s with %s\n",*asp,nbname)); >+ if (strcasecmp(*asp,nbname)==0) >+ { >+ specialipisvalid = true; >+ } >+ asp++; >+ } >+ } >+ } >+ } >+ } >+ talloc_free(mytmp_ctx); >+ } >+ else >+ { >+ DEBUG(1, ("lpcfg_workstation_name_ip_encoding is not enabled\n")); >+ } >+ >+ if (!isspecialip) >+ { >+ //solution 2 >+ //We are not using specially encoded IPs but a workstation name >+ //so expect the admin to have configured reverse lookups properly >+ //WINS or anything else easily spoofable should be not used as we >+ //do a reverse lookup and verify whether it matches the claimed >+ //workstation name >+ >+ //As this also depends on the IP it is as insecure as the other way >+ //but it is providing an easy method if reverse lookups are configured properly >+ //and thus might need no changes on the client side >+ >+ //As this implementation has no access to the userWorkstations attribute >+ //and it should not break BYOD solutions for non-restricted user accounts >+ //we allow the admin to configure the names of the user accounts >+ //which always need this verification. >+ //As expanding groups currently doesn't work there is a second option >+ //to define an user prefix to add many users at once if they >+ //have the same prefix >+ >+ //To handle dual boot situations we allow a configurable list of suffixes >+ //which the client might add to its reverse lookup DNS name >+ //to allow both OS to be joined independently to the domain >+ >+ >+ const char **restrictedusers = lpcfg_workstation_restricted_users(gensec_security->settings->lp_ctx); >+ const char **restrictedusersprefix = lpcfg_workstation_restricted_users_prefix(gensec_security->settings->lp_ctx); >+ bool isrestricted = false, foundbyprefix=false; >+ >+ if (restrictedusers != NULL){ >+ DEBUG(1, ("Restricted users list exists\n")); >+ >+ while (*restrictedusers != NULL) >+ { >+ DEBUG(1, ("Compare restricted user\n")); >+ >+ DEBUG(1, ("Compare %s with %s\n",*restrictedusers,ntlmssp_state->user)); >+ //TODO: expand groups, etc?!!! >+ if (strcasecmp(*restrictedusers,ntlmssp_state->user)==0) //make usernames case insensitive >+ { >+ isrestricted = true; >+ } >+ restrictedusers++; >+ }} >+ else DEBUG(1, ("Restricted users list doesn't exist\n")); >+ if (isrestricted) DEBUG(1, ("Restricted user found\n")); >+ else DEBUG(1, ("User is not in restricted list\n")); >+ >+ if (restrictedusersprefix != NULL){ >+ DEBUG(1, ("Restricted users prefix list exists\n")); >+ >+ while (*restrictedusersprefix != NULL) >+ { >+ int len=strlen(*restrictedusersprefix); >+ DEBUG(1, ("Compare restricted user with prefix list\n")); >+ DEBUG(1, ("Compare prefix %s with %s\n",*restrictedusersprefix,ntlmssp_state->user)); >+ //TODO: expand groups, etc?!!! >+ if (strncasecmp(*restrictedusersprefix,ntlmssp_state->user,len)==0) //make usernames case insensitive >+ { >+ isrestricted = true; >+ foundbyprefix = true; >+ } >+ restrictedusersprefix++; >+ }} >+ else DEBUG(1, ("Restricted users prefix list doesn't exist\n")); >+ if (foundbyprefix) DEBUG(1, ("Restricted user found by prefix\n")); >+ else DEBUG(1, ("User is not in restricted prefix list\n")); >+ >+ >+ if (isrestricted) >+ { >+ >+ TALLOC_CTX *mytmp_ctx = talloc_new(NULL); >+ char *origip; >+ struct sockaddr_in addr; >+ char hbuf[NI_MAXHOST]; >+ origip = tsocket_address_inet_addr_string(remoteaddr,mytmp_ctx); >+ addr.sin_family = AF_INET; >+ addr.sin_port = htons(tsocket_address_inet_port(remoteaddr));//simply useless >+ inet_pton(AF_INET,origip,&addr.sin_addr); >+ >+ //Reuse function to validate whether caller ip is private >+ //If it is not private we probably can't control reverse lookup and >+ //so we alway fail as servers shouldn't be accessible from the internet anyway >+ if (!isencodedprivateip(origip,'.'))return NT_STATUS_INVALID_WORKSTATION; >+ >+ DEBUG(1, ("NO SPECIAL IP_1\n")); >+ if (getnameinfo((struct sockaddr *)&addr,sizeof(addr),hbuf,sizeof(hbuf), >+ NULL, 0, NI_NAMEREQD|NI_NOFQDN)) //as userWorkstation is probably without FQDN >+ { >+ DEBUG(1, ("REVERSE LOOKUP FAILED\n")); >+ return NT_STATUS_INVALID_WORKSTATION; >+ } >+ else >+ { >+ const char **allowednamesuffixes = lpcfg_workstation_name_suffix_restricted_users(gensec_security->settings->lp_ctx); >+ bool wsfound=false; >+ if (allowednamesuffixes != NULL){ >+ DEBUG(1, ("Workstation name suffix list exists\n")); >+ >+ while ((*allowednamesuffixes != NULL)&&(!wsfound)) >+ { >+ int suffixlen=strlen(*allowednamesuffixes); >+ int lookuplen=strlen(hbuf); >+ int nblen=strlen(ntlmssp_state->client.netbios_name); >+ >+ DEBUG(1, ("Compare hbuf %s and suffix %s with %s\n",hbuf,*allowednamesuffixes,ntlmssp_state->client.netbios_name)); >+ if ((lookuplen+suffixlen)==nblen) >+ if (strncasecmp(hbuf,ntlmssp_state->client.netbios_name,lookuplen)==0) >+ { >+ const char *p=ntlmssp_state->client.netbios_name; >+ p+=lookuplen; >+ if (strcasecmp(p,*allowednamesuffixes)==0){ >+ wsfound = true; >+ DEBUG(1, ("Workstation matched with suffix\n")); >+ >+ } >+ } >+ allowednamesuffixes++; >+ } >+ //We only temporarily store fail or success >+ //In case of fail nothing to return as the next verification will do that >+ } >+ if (!wsfound) >+ if (strlen(hbuf)!=strlen(ntlmssp_state->client.netbios_name)) >+ { >+ DEBUG(1, ("%s can't match %s\n",hbuf,ntlmssp_state->client.netbios_name)); >+ //Should be replaced with a description to prevent printing externally controlled characters >+ return NT_STATUS_INVALID_WORKSTATION; //NAME CAN't MATCH >+ } >+ else >+ { >+ for (int i=0; i<strlen(hbuf); i++) >+ { >+ if (toupper(hbuf[i])!=toupper(ntlmssp_state->client.netbios_name[i])) >+ { >+ //Should be replaced with a description to prevent printing externally controlled characters >+ DEBUG(1, ("%s can't match %s\n",hbuf,ntlmssp_state->client.netbios_name)); >+ return NT_STATUS_INVALID_WORKSTATION; //NAME DOESN'T MATCH >+ } >+ } >+ } >+ } >+ talloc_free(mytmp_ctx); >+ } >+ } >+ else if (!specialipisvalid){ >+ DEBUG(1, ("SPECIAL_IP_WORKSTATIONNAME didn't match IP")); >+ return NT_STATUS_INVALID_WORKSTATION; >+ } >+ } >+ else >+ { >+ DEBUG(1, ("FIXME: Currently we can only handle IPv4, this breaks IPv6\n")); >+ >+ //This should be moved somewhere else >+ //to break IPv6 only for users with userWorkstation verification >+ return NT_STATUS_INVALID_WORKSTATION; >+ } >+ > talloc_steal(state, state->encrypted_session_key.data); > > if (auth_flags != 0) {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 14385
: 15985 |
15986