1 /* UNIX SMBlib NetBIOS implementation
4 SMBlib Utility Routines
6 Copyright (C) Richard Sharpe 1996
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "smblib-priv.h"
30 /* Print out an SMB pkt in all its gory detail ... */
32 void SMB_Print_Pkt(FILE fd, RFCNB_Pkt *pkt, BOOL command, int Offset, int Len)
36 /* Well, just how do we do this ... print it I suppose */
38 /* Print out the SMB header ... */
40 /* Print the command */
42 /* Print the other bits in the header */
49 /* Convert a DOS Date_Time to a local host type date time for printing */
51 char *SMB_DOSTimToStr(int DOS_time)
53 { static char SMB_Time_Temp[48];
54 int DOS_sec, DOS_min, DOS_hour, DOS_day, DOS_month, DOS_year;
58 DOS_sec = (DOS_time & 0x001F) * 2;
59 DOS_min = (DOS_time & 0x07E0) >> 5;
60 DOS_hour = ((DOS_time & 0xF800) >> 11);
62 DOS_day = (DOS_time & 0x001F0000) >> 16;
63 DOS_month = (DOS_time & 0x01E00000) >> 21;
64 DOS_year = ((DOS_time & 0xFE000000) >> 25) + 80;
66 sprintf(SMB_Time_Temp, "%2d/%02d/%2d %2d:%02d:%02d", DOS_day, DOS_month,
67 DOS_year, DOS_hour, DOS_min, DOS_sec);
69 return(SMB_Time_Temp);
73 /* Convert an attribute byte/word etc to a string ... We return a pointer
74 to a static string which we guarantee is long enough. If verbose is
75 true, we print out long form of strings ... */
77 char *SMB_AtrToStr(int attribs, BOOL verbose)
79 { static char SMB_Attrib_Temp[128];
81 SMB_Attrib_Temp[0] = 0;
83 if (attribs & SMB_FA_ROF)
84 strcat(SMB_Attrib_Temp, (verbose?"Read Only ":"R"));
86 if (attribs & SMB_FA_HID)
87 strcat(SMB_Attrib_Temp, (verbose?"Hidden ":"H"));
89 if (attribs & SMB_FA_SYS)
90 strcat(SMB_Attrib_Temp, (verbose?"System ":"S"));
92 if (attribs & SMB_FA_VOL)
93 strcat(SMB_Attrib_Temp, (verbose?"Volume ":"V"));
95 if (attribs & SMB_FA_DIR)
96 strcat(SMB_Attrib_Temp, (verbose?"Directory ":"D"));
98 if (attribs & SMB_FA_ARC)
99 strcat(SMB_Attrib_Temp, (verbose?"Archive ":"A"));
101 return(SMB_Attrib_Temp);
105 /* Pick up the Max Buffer Size from the Tree Structure ... */
107 int SMB_Get_Tree_MBS(SMB_Tree_Handle tree)
118 /* Pick up the Max buffer size */
120 int SMB_Get_Max_Buf_Siz(SMB_Handle_Type Con_Handle)
123 if (Con_Handle != NULL) {
124 return(Con_Handle -> max_xmit);
131 /* Pickup the protocol index from the connection structure */
133 int SMB_Get_Protocol_IDX(SMB_Handle_Type Con_Handle)
136 if (Con_Handle != NULL) {
137 return(Con_Handle -> prot_IDX);
140 return(0xFFFF); /* Invalid protocol */
145 /* Pick up the protocol from the connection structure */
147 int SMB_Get_Protocol(SMB_Handle_Type Con_Handle)
150 if (Con_Handle != NULL) {
151 return(Con_Handle -> protocol);
154 return(0xFFFF); /* Invalid protocol */
159 /* Figure out what protocol was accepted, given the list of dialect strings */
160 /* We offered, and the index back from the server. We allow for a user */
161 /* supplied list, and assume that it is a subset of our list */
163 int SMB_Figure_Protocol(char *dialects[], int prot_index)
167 if (dialects == SMB_Prots) { /* The jobs is easy, just index into table */
169 return(SMB_Types[prot_index]);
171 else { /* Search through SMB_Prots looking for a match */
173 for (i = 0; SMB_Prots[i] != NULL; i++) {
175 if (strcmp(dialects[prot_index], SMB_Prots[i]) == 0) { /* A match */
177 return(SMB_Types[i]);
183 /* If we got here, then we are in trouble, because the protocol was not */
184 /* One we understand ... */
186 return(SMB_P_Unknown);
193 /* Negotiate the protocol we will use from the list passed in Prots */
194 /* we return the index of the accepted protocol in NegProt, -1 indicates */
195 /* none acceptible, and our return value is 0 if ok, <0 if problems */
197 int SMB_Negotiate(SMB_Handle_Type Con_Handle, char *Prots[])
199 { struct SMB_Neg_Prot_Def *prot_pkt;
200 struct SMB_Neg_Prot_Resp_Def *resp_pkt;
201 struct RFCNB_Pkt *pkt;
202 int prots_len, i, pkt_len, prot, alloc_len;
205 /* Figure out how long the prot list will be and allocate space for it */
209 for (i = 0; Prots[i] != NULL; i++) {
211 prots_len = prots_len + strlen(Prots[i]) + 2; /* Account for null etc */
215 /* The -1 accounts for the one byte smb_buf we have because some systems */
216 /* don't like char msg_buf[] */
218 pkt_len = SMB_negp_len + prots_len;
220 /* Make sure that the pkt len is long enough for the max response ... */
221 /* Which is a problem, because the encryption key len eec may be long */
223 if (pkt_len < (SMB_hdr_wct_offset + (19 * 2) + 40)) {
225 alloc_len = SMB_hdr_wct_offset + (19 * 2) + 40;
234 pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(alloc_len);
238 SMBlib_errno = SMBlibE_NoSpace;
243 /* Now plug in the bits we need */
245 bzero(SMB_Hdr(pkt), SMB_negp_len);
246 SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
247 *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBnegprot;
248 SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle -> pid);
249 SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
250 SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle -> mid);
251 SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle -> uid);
252 *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
254 SSVAL(SMB_Hdr(pkt), SMB_negp_bcc_offset, prots_len);
256 /* Now copy the prot strings in with the right stuff */
258 p = (char *)(SMB_Hdr(pkt) + SMB_negp_buf_offset);
260 for (i = 0; Prots[i] != NULL; i++) {
263 strcpy(p + 1, Prots[i]);
264 p = p + strlen(Prots[i]) + 2; /* Adjust len of p for null plus dialectID */
268 /* Now send the packet and sit back ... */
270 if (RFCNB_Send(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0){
274 fprintf(stderr, "Error sending negotiate protocol\n");
278 SMBlib_errno = -SMBlibE_SendFailed; /* Failed, check lower layer errno */
283 /* Now get the response ... */
285 if (RFCNB_Recv(Con_Handle -> Trans_Connect, pkt, alloc_len) < 0) {
288 fprintf(stderr, "Error receiving response to negotiate\n");
292 SMBlib_errno = -SMBlibE_RecvFailed; /* Failed, check lower layer errno */
297 if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
300 fprintf(stderr, "SMB_Negotiate failed with errorclass = %i, Error Code = %i\n",
301 CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
302 SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
305 SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
307 SMBlib_errno = SMBlibE_Remote;
312 if (SVAL(SMB_Hdr(pkt), SMB_negrCP_idx_offset) == 0xFFFF) {
315 fprintf(stderr, "None of our protocols was accepted ... ");
319 SMBlib_errno = SMBlibE_NegNoProt;
324 /* Now, unpack the info from the response, if any and evaluate the proto */
325 /* selected. We must make sure it is one we like ... */
327 Con_Handle -> prot_IDX = prot = SVAL(SMB_Hdr(pkt), SMB_negrCP_idx_offset);
328 Con_Handle -> protocol = SMB_Figure_Protocol(Prots, prot);
330 if (Con_Handle -> protocol == SMB_P_Unknown) { /* No good ... */
333 SMBlib_errno = SMBlibE_ProtUnknown;
338 switch (CVAL(SMB_Hdr(pkt), SMB_hdr_wct_offset)) {
340 case 0x01: /* No more info ... */
344 case 13: /* Up to and including LanMan 2.1 */
346 Con_Handle -> Security = SVAL(SMB_Hdr(pkt), SMB_negrLM_sec_offset);
347 Con_Handle -> encrypt_passwords = ((Con_Handle -> Security & SMB_sec_encrypt_mask) != 0x00);
348 Con_Handle -> Security = Con_Handle -> Security & SMB_sec_user_mask;
350 Con_Handle -> max_xmit = SVAL(SMB_Hdr(pkt), SMB_negrLM_mbs_offset);
351 Con_Handle -> MaxMPX = SVAL(SMB_Hdr(pkt), SMB_negrLM_mmc_offset);
352 Con_Handle -> MaxVC = SVAL(SMB_Hdr(pkt), SMB_negrLM_mnv_offset);
353 Con_Handle -> Raw_Support = SVAL(SMB_Hdr(pkt), SMB_negrLM_rm_offset);
354 Con_Handle -> SessionKey = IVAL(SMB_Hdr(pkt), SMB_negrLM_sk_offset);
355 Con_Handle -> SvrTZ = SVAL(SMB_Hdr(pkt), SMB_negrLM_stz_offset);
356 Con_Handle -> Encrypt_Key_Len = SVAL(SMB_Hdr(pkt), SMB_negrLM_ekl_offset);
358 p = (SMB_Hdr(pkt) + SMB_negrLM_buf_offset);
359 fprintf(stderr, "%d", (char *)(SMB_Hdr(pkt) + SMB_negrLM_buf_offset));
360 memcpy(Con_Handle->Encrypt_Key, p, 8);
362 p = (SMB_Hdr(pkt) + SMB_negrLM_buf_offset + Con_Handle -> Encrypt_Key_Len);
364 strncpy(p, Con_Handle -> Svr_PDom, sizeof(Con_Handle -> Svr_PDom) - 1);
368 case 17: /* NT LM 0.12 and LN LM 1.0 */
370 Con_Handle -> Security = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_sec_offset);
371 Con_Handle -> encrypt_passwords = ((Con_Handle -> Security & SMB_sec_encrypt_mask) != 0x00);
372 Con_Handle -> Security = Con_Handle -> Security & SMB_sec_user_mask;
374 Con_Handle -> max_xmit = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_mbs_offset);
375 Con_Handle -> MaxMPX = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_mmc_offset);
376 Con_Handle -> MaxVC = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_mnv_offset);
377 Con_Handle -> MaxRaw = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_mrs_offset);
378 Con_Handle -> SessionKey = IVAL(SMB_Hdr(pkt), SMB_negrNTLM_sk_offset);
379 Con_Handle -> SvrTZ = SVAL(SMB_Hdr(pkt), SMB_negrNTLM_stz_offset);
380 Con_Handle -> Encrypt_Key_Len = CVAL(SMB_Hdr(pkt), SMB_negrNTLM_ekl_offset);
382 p = (SMB_Hdr(pkt) + SMB_negrNTLM_buf_offset );
383 memcpy(Con_Handle -> Encrypt_Key, p, 8);
384 p = (SMB_Hdr(pkt) + SMB_negrNTLM_buf_offset + Con_Handle -> Encrypt_Key_Len);
386 strncpy(p, Con_Handle -> Svr_PDom, sizeof(Con_Handle -> Svr_PDom) - 1);
393 fprintf(stderr, "Unknown NegProt response format ... Ignored\n");
394 fprintf(stderr, " wct = %i\n", CVAL(SMB_Hdr(pkt), SMB_hdr_wct_offset));
401 fprintf(stderr, "Protocol selected is: %i:%s\n", prot, Prots[prot]);
409 /* Get our hostname */
411 void SMB_Get_My_Name(char *name, int len)
415 if (gethostname(name, len) < 0) { /* Error getting name */
417 strncpy(name, "unknown", len);
419 /* Should check the error */
422 fprintf(stderr, "gethostname in SMB_Get_My_Name returned error:");
428 /* only keep the portion up to the first "." */
433 /* Send a TCON to the remote server ... */
435 SMB_Tree_Handle SMB_TreeConnect(SMB_Handle_Type Con_Handle,
436 SMB_Tree_Handle Tree_Handle,
441 { struct RFCNB_Pkt *pkt;
442 int param_len, i, pkt_len;
444 SMB_Tree_Handle tree;
446 /* Figure out how much space is needed for path, password, dev ... */
448 if (path == NULL | password == NULL | device == NULL) {
451 fprintf(stderr, "Bad parameter passed to SMB_TreeConnect\n");
454 SMBlib_errno = SMBlibE_BadParam;
459 /* The + 2 is because of the \0 and the marker ... */
461 param_len = strlen(path) + 2 + strlen(password) + 2 + strlen(device) + 2;
463 /* The -1 accounts for the one byte smb_buf we have because some systems */
464 /* don't like char msg_buf[] */
466 pkt_len = SMB_tcon_len + param_len;
468 pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len);
472 SMBlib_errno = SMBlibE_NoSpace;
473 return(NULL); /* Should handle the error */
477 /* Now allocate a tree for this to go into ... */
479 if (Tree_Handle == NULL) {
481 tree = (SMB_Tree_Handle)malloc(sizeof(struct SMB_Tree_Structure));
486 SMBlib_errno = SMBlibE_NoSpace;
497 tree -> next = tree -> prev = NULL;
498 tree -> con = Con_Handle;
499 strncpy(tree -> path, path, sizeof(tree -> path));
500 strncpy(tree -> device_type, device, sizeof(tree -> device_type));
502 /* Now plug in the values ... */
504 bzero(SMB_Hdr(pkt), SMB_tcon_len);
505 SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
506 *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtcon;
507 SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle -> pid);
508 SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
509 SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle -> mid);
510 SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle -> uid);
511 *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
513 SSVAL(SMB_Hdr(pkt), SMB_tcon_bcc_offset, param_len);
515 /* Now copy the param strings in with the right stuff */
517 p = (char *)(SMB_Hdr(pkt) + SMB_tcon_buf_offset);
520 p = p + strlen(path) + 2;
522 strcpy(p + 1, password);
523 p = p + strlen(password) + 2;
525 strcpy(p + 1, device);
527 /* Now send the packet and sit back ... */
529 if (RFCNB_Send(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0){
532 fprintf(stderr, "Error sending TCon request\n");
535 if (Tree_Handle == NULL)
538 SMBlib_errno = -SMBlibE_SendFailed;
543 /* Now get the response ... */
545 if (RFCNB_Recv(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0) {
548 fprintf(stderr, "Error receiving response to TCon\n");
551 if (Tree_Handle == NULL)
554 SMBlib_errno = -SMBlibE_RecvFailed;
559 /* Check out the response type ... */
561 if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
564 fprintf(stderr, "SMB_TCon failed with errorclass = %i, Error Code = %i\n",
565 CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
566 SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
569 if (Tree_Handle == NULL)
571 SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
573 SMBlib_errno = SMBlibE_Remote;
578 tree -> tid = SVAL(SMB_Hdr(pkt), SMB_tconr_tid_offset);
579 tree -> mbs = SVAL(SMB_Hdr(pkt), SMB_tconr_mbs_offset);
582 fprintf(stderr, "TConn succeeded, with TID=%i, Max Xmit=%i\n",
583 tree -> tid, tree -> mbs);
586 /* Now link the Tree to the Server Structure ... */
588 if (Con_Handle -> first_tree == NULL) {
590 Con_Handle -> first_tree == tree;
591 Con_Handle -> last_tree == tree;
596 Con_Handle -> last_tree -> next = tree;
597 tree -> prev = Con_Handle -> last_tree;
598 Con_Handle -> last_tree = tree;
607 int SMB_TreeDisconnect(SMB_Tree_Handle Tree_Handle, BOOL discard)
609 { struct RFCNB_Pkt *pkt;
612 pkt_len = SMB_tdis_len;
614 pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len);
618 SMBlib_errno = SMBlibE_NoSpace;
619 return(SMBlibE_BAD); /* Should handle the error */
623 /* Now plug in the values ... */
625 bzero(SMB_Hdr(pkt), SMB_tdis_len);
626 SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF); /* Plunk in IDF */
627 *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBtdis;
628 SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Tree_Handle -> con -> pid);
629 SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Tree_Handle -> con -> mid);
630 SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Tree_Handle -> con -> uid);
631 *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 0;
633 SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, Tree_Handle -> tid);
634 SSVAL(SMB_Hdr(pkt), SMB_tcon_bcc_offset, 0);
636 /* Now send the packet and sit back ... */
638 if (RFCNB_Send(Tree_Handle -> con -> Trans_Connect, pkt, pkt_len) < 0){
641 fprintf(stderr, "Error sending TDis request\n");
645 SMBlib_errno = -SMBlibE_SendFailed;
650 /* Now get the response ... */
652 if (RFCNB_Recv(Tree_Handle -> con -> Trans_Connect, pkt, pkt_len) < 0) {
655 fprintf(stderr, "Error receiving response to TCon\n");
659 SMBlib_errno = -SMBlibE_RecvFailed;
664 /* Check out the response type ... */
666 if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) { /* Process error */
669 fprintf(stderr, "SMB_TDis failed with errorclass = %i, Error Code = %i\n",
670 CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
671 SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
674 SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
676 SMBlib_errno = SMBlibE_Remote;
681 Tree_Handle -> tid = 0xFFFF; /* Invalid TID */
682 Tree_Handle -> mbs = 0; /* Invalid */
686 fprintf(stderr, "Tree disconnect successful ...\n");
690 /* What about the tree handle ? */
692 if (discard == TRUE) { /* Unlink it and free it ... */
694 if (Tree_Handle -> next == NULL)
695 Tree_Handle -> con -> first_tree = Tree_Handle -> prev;
697 Tree_Handle -> next -> prev = Tree_Handle -> prev;
699 if (Tree_Handle -> prev == NULL)
700 Tree_Handle -> con -> last_tree = Tree_Handle -> next;
702 Tree_Handle -> prev -> next = Tree_Handle -> next;
711 /* Pick up the last LMBlib error ... */
713 int SMB_Get_Last_Error()
717 return(SMBlib_errno);
721 /* Pick up the last error returned in an SMB packet */
722 /* We will need macros to extract error class and error code */
724 int SMB_Get_Last_SMB_Err()
728 return(SMBlib_SMB_Error);
732 /* Pick up the error message associated with an error from SMBlib */
734 /* Keep this table in sync with the message codes in smblib-common.h */
736 static const char *SMBlib_Error_Messages[] = {
738 "Request completed sucessfully.",
739 "Server returned a non-zero SMB Error Class and Code.",
740 "A lower layer protocol error occurred.",
741 "Function not yet implemented.",
742 "The protocol negotiated does not support the request.",
743 "No space available for operation.",
744 "One or more bad parameters passed.",
745 "None of the protocols we offered were accepted.",
746 "The attempt to send an SMB request failed. See protocol error info.",
747 "The attempt to get an SMB response failed. See protocol error info.",
748 "The logon request failed, but you were logged in as guest.",
749 "The attempt to call the remote server failed. See protocol error info.",
750 "The protocol dialect specified in a NegProt and accepted by the server is unknown.",
751 /* This next one simplifies error handling */
752 "No such error code.",
755 int SMB_Get_Error_Msg(int msg, char *msgbuf, int len)
762 SMBlib_Error_Messages[msg>SMBlibE_NoSuchMsg?SMBlibE_NoSuchMsg:msg],
764 msgbuf[len - 1] = 0; /* Make sure it is a string */
766 else { /* Add the lower layer message ... */
770 msg = -msg; /* Make it positive */
773 SMBlib_Error_Messages[msg>SMBlibE_NoSuchMsg?SMBlibE_NoSuchMsg:msg],
776 msgbuf[len - 1] = 0; /* make sure it is a string */
778 if (strlen(msgbuf) < len) { /* If there is space, put rest in */
780 strncat(msgbuf, "\n\t", len - strlen(msgbuf));
782 RFCNB_Get_Error(prot_msg, sizeof(prot_msg) - 1);
784 strncat(msgbuf, prot_msg, len - strlen(msgbuf));