Pull fix from branch_1_1
[freeradius.git] / src / modules / rlm_smb / smblib.c
1 /* UNIX SMBlib NetBIOS implementation
2
3    Version 1.0
4    SMBlib Routines
5
6    Copyright (C) Richard Sharpe 1996
7    Copyright 2006 The FreeRADIUS server project
8
9 */
10
11 /*
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 2 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27 #include <freeradius-devel/ident.h>
28 RCSID("$Id$")
29
30 #include <ctype.h>
31 #include <string.h>
32 #include "smblib-priv.h"
33 #include "rfcnb.h"
34
35 #define SMBLIB_ERRNO
36 #define uchar unsigned char
37
38 int SMBlib_errno;
39 int SMBlib_SMB_Error;
40 SMB_State_Types SMBlib_State;
41
42 /* Initialize the SMBlib package     */
43
44 int SMB_Init()
45
46 {
47
48   SMBlib_State = SMB_State_Started;
49
50   signal(SIGPIPE, SIG_IGN);   /* Ignore these ... */
51
52 /* If SMBLIB_Instrument is defines, turn on the instrumentation stuff */
53 #ifdef SMBLIB_INSTRUMENT
54
55   SMBlib_Instrument_Init();
56
57 #endif
58
59   return 0;
60
61 }
62
63 int SMB_Term()
64
65 {
66
67 #ifdef SMBLIB_INSTRUMENT
68
69   SMBlib_Instrument_Term();       /* Clean up and print results */
70
71 #endif
72
73   return 0;
74
75 }
76
77 /* SMB_Create: Create a connection structure and return for later use */
78 /* We have other helper routines to set variables                     */
79
80 SMB_Handle_Type SMB_Create_Con_Handle()
81
82 {
83
84   SMBlib_errno = SMBlibE_NotImpl;
85   return(NULL);
86
87 }
88
89 int SMBlib_Set_Sock_NoDelay(SMB_Handle_Type Con_Handle, BOOL yn)
90
91 {
92
93
94   if (RFCNB_Set_Sock_NoDelay(Con_Handle -> Trans_Connect, yn) < 0) {
95
96 #ifdef DEBUG
97 #endif
98
99     fprintf(stderr, "Setting no-delay on TCP socket failed ...\n");
100
101   }
102
103   return(0);
104
105 }
106
107 /* SMB_Connect_Server: Connect to a server, but don't negotiate protocol */
108 /* or anything else ...                                                  */
109
110 SMB_Handle_Type SMB_Connect_Server(SMB_Handle_Type Con_Handle,
111                                    char *server, char *NTdomain)
112
113 {
114   SMB_Handle_Type con;
115   char called[80], calling[80], *address;
116   int i;
117
118   /* Get a connection structure if one does not exist */
119
120   con = Con_Handle;
121
122   if (Con_Handle == NULL) {
123
124     if ((con = (struct SMB_Connect_Def *)malloc(sizeof(struct SMB_Connect_Def))) == NULL) {
125
126
127       SMBlib_errno = SMBlibE_NoSpace;
128       return NULL;
129     }
130
131   }
132
133   /* Init some things ... */
134
135   strcpy(con -> service, "");
136   strcpy(con -> username, "");
137   strcpy(con -> password, "");
138   strcpy(con -> sock_options, "");
139   strcpy(con -> address, "");
140   strcpy(con -> desthost, server);
141   strcpy(con -> PDomain, NTdomain);
142   strcpy(con -> OSName, SMBLIB_DEFAULT_OSNAME);
143   strcpy(con -> LMType, SMBLIB_DEFAULT_LMTYPE);
144   con -> first_tree = con -> last_tree = NULL;
145
146   SMB_Get_My_Name(con -> myname, sizeof(con -> myname));
147
148   con -> port = 0;                    /* No port selected */
149
150   /* Get some things we need for the SMB Header */
151
152   con -> pid = getpid();
153   con -> mid = con -> pid;      /* This will do for now ... */
154   con -> uid = 0;               /* Until we have done a logon, no uid ... */
155   con -> gid = getgid();
156
157   /* Now connect to the remote end, but first upper case the name of the
158      service we are going to call, sine some servers want it in uppercase */
159
160   for (i=0; i < strlen(server); i++)
161     called[i] = toupper(server[i]);
162
163   called[strlen(server)] = 0;    /* Make it a string */
164
165   for (i=0; i < strlen(con -> myname); i++)
166     calling[i] = toupper(con -> myname[i]);
167
168   calling[strlen(con -> myname)] = 0;    /* Make it a string */
169
170   if (strcmp(con -> address, "") == 0)
171     address = con -> desthost;
172   else
173     address = con -> address;
174
175   con -> Trans_Connect = RFCNB_Call(called,
176                                     calling,
177                                     address, /* Protocol specific */
178                                     con -> port);
179
180   /* Did we get one? */
181
182   if (con -> Trans_Connect == NULL) {
183
184     if (Con_Handle == NULL) {
185       Con_Handle = NULL;
186       free(con);
187     }
188     SMBlib_errno = -SMBlibE_CallFailed;
189     return NULL;
190
191   }
192
193   return(con);
194
195 }
196
197 /* SMB_Connect: Connect to the indicated server                       */
198 /* If Con_Handle == NULL then create a handle and connect, otherwise  */
199 /* use the handle passed                                              */
200
201 static const char *SMB_Prots_Restrict[] = {"PC NETWORK PROGRAM 1.0",
202                                           NULL};
203
204
205 SMB_Handle_Type SMB_Connect(SMB_Handle_Type Con_Handle,
206                             SMB_Tree_Handle *tree,
207                             char *service,
208                             char *username,
209                             char *password)
210
211 { SMB_Handle_Type con;
212   char *host, *address;
213   char temp[80], called[80], calling[80];
214   int i;
215
216   /* Get a connection structure if one does not exist */
217
218   con = Con_Handle;
219
220   if (Con_Handle == NULL) {
221
222     if ((con = (struct SMB_Connect_Def *)malloc(sizeof(struct SMB_Connect_Def))) == NULL) {
223
224       SMBlib_errno = SMBlibE_NoSpace;
225       return NULL;
226     }
227
228   }
229
230   /* Init some things ... */
231
232   strcpy(con -> service, service);
233   strcpy(con -> username, username);
234   strcpy(con -> password, password);
235   strcpy(con -> sock_options, "");
236   strcpy(con -> address, "");
237   strcpy(con -> PDomain, SMBLIB_DEFAULT_DOMAIN);
238   strcpy(con -> OSName, SMBLIB_DEFAULT_OSNAME);
239   strcpy(con -> LMType, SMBLIB_DEFAULT_LMTYPE);
240   con -> first_tree = con -> last_tree = NULL;
241
242   SMB_Get_My_Name(con -> myname, sizeof(con -> myname));
243
244   con -> port = 0;                    /* No port selected */
245
246   /* Get some things we need for the SMB Header */
247
248   con -> pid = getpid();
249   con -> mid = con -> pid;      /* This will do for now ... */
250   con -> uid = 0;               /* Until we have done a logon, no uid */
251   con -> gid = getgid();
252
253   /* Now figure out the host portion of the service */
254
255   strcpy(temp, service);
256   host = strtok(temp, "/\\");     /* Separate host name portion */
257   strcpy(con -> desthost, host);
258
259   /* Now connect to the remote end, but first upper case the name of the
260      service we are going to call, sine some servers want it in uppercase */
261
262   for (i=0; i < strlen(host); i++)
263     called[i] = toupper(host[i]);
264
265   called[strlen(host)] = 0;    /* Make it a string */
266
267   for (i=0; i < strlen(con -> myname); i++)
268     calling[i] = toupper(con -> myname[i]);
269
270   calling[strlen(con -> myname)] = 0;    /* Make it a string */
271
272   if (strcmp(con -> address, "") == 0)
273     address = con -> desthost;
274   else
275     address = con -> address;
276
277   con -> Trans_Connect = RFCNB_Call(called,
278                                     calling,
279                                     address, /* Protocol specific */
280                                     con -> port);
281
282   /* Did we get one? */
283
284   if (con -> Trans_Connect == NULL) {
285
286     if (Con_Handle == NULL) {
287       free(con);
288       Con_Handle = NULL;
289     }
290     SMBlib_errno = -SMBlibE_CallFailed;
291     return NULL;
292
293   }
294
295   /* Now, negotiate the protocol */
296
297   if (SMB_Negotiate(con, SMB_Prots_Restrict) < 0) {
298
299     /* Hmmm what should we do here ... We have a connection, but could not
300        negotiate ...                                                      */
301
302     return NULL;
303
304   }
305
306   /* Now connect to the service ... */
307
308   if ((*tree = SMB_TreeConnect(con, NULL, service, password, "A:")) == NULL) {
309
310     return NULL;
311
312   }
313
314   return(con);
315
316 }
317
318 /* Logon to the server. That is, do a session setup if we can. We do not do */
319 /* Unicode yet!                                                             */
320
321 int SMB_Logon_Server(SMB_Handle_Type Con_Handle, char *UserName,
322                      char *PassWord)
323
324 {
325   struct RFCNB_Pkt *pkt;
326   int param_len, pkt_len, pass_len;
327   char *p, pword[256];
328
329   /* First we need a packet etc ... but we need to know what protocol has  */
330   /* been negotiated to figure out if we can do it and what SMB format to  */
331   /* use ...                                                               */
332
333   if (Con_Handle -> protocol < SMB_P_LanMan1) {
334
335     SMBlib_errno = SMBlibE_ProtLow;
336     return(SMBlibE_BAD);
337
338   }
339
340   strcpy(pword, PassWord);
341   if (Con_Handle -> encrypt_passwords)
342   {
343     pass_len=24;
344     SMBencrypt((uchar *) PassWord, (uchar *)Con_Handle -> Encrypt_Key,(uchar *)pword);
345   }
346   else
347         pass_len=strlen(pword);
348
349
350   /* Now build the correct structure */
351
352   if (Con_Handle -> protocol < SMB_P_NT1) {
353
354     param_len = strlen(UserName) + 1 + pass_len + 1 +
355                 strlen(Con_Handle -> PDomain) + 1 +
356                 strlen(Con_Handle -> OSName) + 1;
357
358     pkt_len = SMB_ssetpLM_len + param_len;
359
360     pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len);
361
362     if (pkt == NULL) {
363
364       SMBlib_errno = SMBlibE_NoSpace;
365       return(SMBlibE_BAD); /* Should handle the error */
366
367     }
368
369     bzero(SMB_Hdr(pkt), SMB_ssetpLM_len);
370     SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF);  /* Plunk in IDF */
371     *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX;
372     SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle -> pid);
373     SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
374     SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle -> mid);
375     SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle -> uid);
376     *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 10;
377     *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF;    /* No extra command */
378     SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0);
379
380     SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mbs_offset, SMBLIB_MAX_XMIT);
381     SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_mmc_offset, 2);
382     SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_vcn_offset, Con_Handle -> pid);
383     SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_snk_offset, 0);
384     SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_pwl_offset, pass_len + 1);
385     SIVAL(SMB_Hdr(pkt), SMB_ssetpLM_res_offset, 0);
386     SSVAL(SMB_Hdr(pkt), SMB_ssetpLM_bcc_offset, param_len);
387
388     /* Now copy the param strings in with the right stuff */
389
390     p = (char *)(SMB_Hdr(pkt) + SMB_ssetpLM_buf_offset);
391
392     /* Copy  in password, then the rest. Password has a null at end */
393
394     memcpy(p, pword, pass_len);
395
396     p = p + pass_len + 1;
397
398     strcpy(p, UserName);
399     p = p + strlen(UserName);
400     *p = 0;
401
402     p = p + 1;
403
404     strcpy(p, Con_Handle -> PDomain);
405     p = p + strlen(Con_Handle -> PDomain);
406     *p = 0;
407     p = p + 1;
408
409     strcpy(p, Con_Handle -> OSName);
410     p = p + strlen(Con_Handle -> OSName);
411     *p = 0;
412
413   }
414   else {
415
416     /* We don't admit to UNICODE support ... */
417
418     param_len = strlen(UserName) + 1 + pass_len +
419                 strlen(Con_Handle -> PDomain) + 1 +
420                 strlen(Con_Handle -> OSName) + 1 +
421                 strlen(Con_Handle -> LMType) + 1;
422
423     pkt_len = SMB_ssetpNTLM_len + param_len;
424
425     pkt = (struct RFCNB_Pkt *)RFCNB_Alloc_Pkt(pkt_len);
426
427     if (pkt == NULL) {
428
429       SMBlib_errno = SMBlibE_NoSpace;
430       return(-1); /* Should handle the error */
431
432     }
433
434     bzero(SMB_Hdr(pkt), SMB_ssetpNTLM_len);
435     SIVAL(SMB_Hdr(pkt), SMB_hdr_idf_offset, SMB_DEF_IDF);  /* Plunk in IDF */
436     *(SMB_Hdr(pkt) + SMB_hdr_com_offset) = SMBsesssetupX;
437     SSVAL(SMB_Hdr(pkt), SMB_hdr_pid_offset, Con_Handle -> pid);
438     SSVAL(SMB_Hdr(pkt), SMB_hdr_tid_offset, 0);
439     SSVAL(SMB_Hdr(pkt), SMB_hdr_mid_offset, Con_Handle -> mid);
440     SSVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset, Con_Handle -> uid);
441     *(SMB_Hdr(pkt) + SMB_hdr_wct_offset) = 13;
442     *(SMB_Hdr(pkt) + SMB_hdr_axc_offset) = 0xFF;    /* No extra command */
443     SSVAL(SMB_Hdr(pkt), SMB_hdr_axo_offset, 0);
444
445     SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mbs_offset, SMBLIB_MAX_XMIT);
446     SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_mmc_offset, 0);
447     SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_vcn_offset, 0);
448     SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_snk_offset, 0);
449     SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cipl_offset, pass_len);
450     SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cspl_offset, 0);
451     SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_res_offset, 0);
452     SIVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_cap_offset, 0);
453     SSVAL(SMB_Hdr(pkt), SMB_ssetpNTLM_bcc_offset, param_len);
454
455     /* Now copy the param strings in with the right stuff */
456
457     p = (char *)(SMB_Hdr(pkt) + SMB_ssetpNTLM_buf_offset);
458
459     /* Copy  in password, then the rest. Password has no null at end */
460
461     memcpy(p, pword, pass_len);
462
463     p = p + pass_len;
464
465     strcpy(p, UserName);
466     p = p + strlen(UserName);
467     *p = 0;
468
469     p = p + 1;
470
471     strcpy(p, Con_Handle -> PDomain);
472     p = p + strlen(Con_Handle -> PDomain);
473   *p = 0;
474     p = p + 1;
475
476     strcpy(p, Con_Handle -> OSName);
477     p = p + strlen(Con_Handle -> OSName);
478     *p = 0;
479     p = p + 1;
480
481     strcpy(p, Con_Handle -> LMType);
482     p = p + strlen(Con_Handle -> LMType);
483     *p = 0;
484
485   }
486
487   /* Now send it and get a response */
488
489   if (RFCNB_Send(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0){
490
491 #ifdef DEBUG
492     fprintf(stderr, "Error sending SessSetupX request\n");
493 #endif
494
495     RFCNB_Free_Pkt(pkt);
496     SMBlib_errno = SMBlibE_SendFailed;
497     return(SMBlibE_BAD);
498
499   }
500
501   /* Now get the response ... */
502
503   if (RFCNB_Recv(Con_Handle -> Trans_Connect, pkt, pkt_len) < 0) {
504
505 #ifdef DEBUG
506     fprintf(stderr, "Error receiving response to SessSetupAndX\n");
507 #endif
508
509     RFCNB_Free_Pkt(pkt);
510     SMBlib_errno = SMBlibE_RecvFailed;
511     return(SMBlibE_BAD);
512
513   }
514
515   /* Check out the response type ... */
516
517   if (CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset) != SMBC_SUCCESS) {  /* Process error */
518
519 #ifdef DEBUG
520     fprintf(stderr, "SMB_SessSetupAndX failed with errorclass = %i, Error Code = %i\n",
521             CVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset),
522             SVAL(SMB_Hdr(pkt), SMB_hdr_err_offset));
523 #endif
524
525     SMBlib_SMB_Error = IVAL(SMB_Hdr(pkt), SMB_hdr_rcls_offset);
526     RFCNB_Free_Pkt(pkt);
527     SMBlib_errno = SMBlibE_Remote;
528     return(SMBlibE_BAD);
529
530   }
531 /** @@@ mdz: check for guest login { **/
532        if (SVAL(SMB_Hdr(pkt), SMB_ssetpr_act_offset) & 0x1)
533        {
534                /* do we allow guest login? NO! */
535                return(SMBlibE_BAD);
536
537        }
538  /** @@@ mdz: } **/
539
540
541 #ifdef DEBUG
542   fprintf(stderr, "SessSetupAndX response. Action = %i\n",
543           SVAL(SMB_Hdr(pkt), SMB_ssetpr_act_offset));
544 #endif
545
546   /* Now pick up the UID for future reference ... */
547
548   Con_Handle -> uid = SVAL(SMB_Hdr(pkt), SMB_hdr_uid_offset);
549   RFCNB_Free_Pkt(pkt);
550
551   return(0);
552
553 }
554
555
556 /* Disconnect from the server, and disconnect all tree connects */
557
558 int SMB_Discon(SMB_Handle_Type Con_Handle, BOOL KeepHandle)
559
560 {
561
562   /* We just disconnect the connection for now ... */
563
564   RFCNB_Hangup(Con_Handle -> Trans_Connect);
565
566   if (!KeepHandle)
567     free(Con_Handle);
568
569   return(0);
570
571 }