1/*
2 * Copyright (c) 2011  Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <libkern/OSAtomic.h>
25#include <sys/smb_apple.h>
26
27#include <netsmb/smb.h>
28#include <netsmb/smb_2.h>
29#include <netsmb/smb_conn.h>
30#include <netsmb/smb_rq.h>
31#include <netsmb/smb_rq_2.h>
32#include <netsmb/smb_packets_2.h>
33#include <smbclient/ntstatus.h>
34
35static int smb2_rq_init_internal(struct smb_rq *rqp, struct smb_connobj *obj,
36                                 u_char cmd, uint32_t *rq_len, int rq_flags,
37                                 vfs_context_t context);
38static int smb2_rq_new(struct smb_rq *rqp);
39
40
41/*
42 * Adds padding to 8 byte boundary for SMB2 compound requests
43 */
44void
45smb2_rq_align8(struct smb_rq *rqp)
46{
47    size_t pad_bytes = 0;
48	struct mbchain *mbp;
49
50    if ((rqp->sr_rq.mb_len % 8) != 0) {
51        /* Next request MUST start on next 8 byte boundary! */
52        pad_bytes = 8 - (rqp->sr_rq.mb_len % 8);
53        smb_rq_getrequest(rqp, &mbp);
54        mb_put_mem(mbp, NULL, pad_bytes, MB_MZERO);
55    }
56}
57
58/*
59 * The object is either a share or a vc in either case the calling routine
60 * must have a reference on the object before calling this routine.
61 *
62 * Be careful to use the correct maco SSTOCP or VCTOCP to get the correct
63 * smb_connobj to pass in which depends on the type of packet you are
64 * sending.  If you pass in the wrong obj, odd behavior will result.
65 */
66int
67smb2_rq_alloc(struct smb_connobj *obj, u_char cmd, uint32_t *rq_len,
68              vfs_context_t context, struct smb_rq **rqpp)
69{
70	struct smb_rq *rqp;
71	int error;
72
73    /*
74     * This function allocates smb_rq and creates the SMB2 header
75     */
76	MALLOC(rqp, struct smb_rq *, sizeof(*rqp), M_SMBRQ, M_WAITOK);
77	if (rqp == NULL)
78		return ENOMEM;
79
80	error = smb2_rq_init_internal(rqp, obj, cmd, rq_len, SMBR_ALLOCED, context);
81	if (!error) {
82		/* On error, smb2_rq_init_internal will free the rqp */
83		*rqpp = rqp;
84	}
85
86	return error;
87}
88
89/*
90 * Similar to smb_rq_bend except matches with smb2_rq_bstart32 meaning it
91 * assumes the use of rqp->sr_lcount
92 */
93void
94smb_rq_bend32(struct smb_rq *rqp)
95{
96	uint32_t bcnt;
97
98	DBG_ASSERT(rqp->sr_lcount);
99	if (rqp->sr_lcount == NULL) {
100		SMBERROR("no lcount\n");	/* actually panic */
101		return;
102	}
103	/*
104	 * Byte Count field should be ignored when dealing with SMB_CAP_LARGE_WRITEX
105	 * or SMB_CAP_LARGE_READX messages. So we set it to zero in these cases.
106	 */
107	if (((VC_CAPS(rqp->sr_vc) & SMB_CAP_LARGE_READX) && (rqp->sr_cmd == SMB_COM_READ_ANDX)) ||
108		((VC_CAPS(rqp->sr_vc) & SMB_CAP_LARGE_WRITEX) && (rqp->sr_cmd == SMB_COM_WRITE_ANDX))) {
109		/* SAMBA 4 doesn't like the byte count to be zero */
110		if (rqp->sr_rq.mb_count > 0xffffffff)
111			bcnt = 0; /* Set the byte count to zero here */
112		else
113			bcnt = (uint32_t)rqp->sr_rq.mb_count;
114	} else if (rqp->sr_rq.mb_count > 0xffffffff) {
115		SMBERROR("byte count too large (%ld)\n", rqp->sr_rq.mb_count);
116		bcnt =  0xfffffff;	/* not sure what else to do here */
117	} else
118		bcnt = (uint32_t)rqp->sr_rq.mb_count;
119
120	*rqp->sr_lcount = htolel(bcnt);
121}
122
123/*
124 * Similar to smb_rq_bstart except uses any uint16 len field
125 */
126void
127smb2_rq_bstart(struct smb_rq *rqp, uint16_t *len_ptr)
128{
129	rqp->sr_bcount = len_ptr;
130	rqp->sr_rq.mb_count = 0;
131}
132
133/*
134 * Similar to smb_rq_bstart except uses any uint32 len field
135 */
136void
137smb2_rq_bstart32(struct smb_rq *rqp, uint32_t *len_ptr)
138{
139	rqp->sr_lcount = len_ptr;
140	rqp->sr_rq.mb_count = 0;
141}
142
143/*
144 * SMB 2.x crediting
145 *
146 * CreditCharge - number of credits consumed by response
147 * CreditRequest/CreditResponse - used to request more credits.
148 *      >1 = requesting more credits
149 *      1 = maintain current number of credits
150 *      0 = decrease number of current credits
151 *
152 * Calls that can use multi credits are
153 *      Read/Write - len is the IO size
154 *      IOCTL - dont think we support any of these specific ones
155 *      Query Dir - output buffer length
156 *      Change Notify - output buffer length
157 *      Query Info - output buffer length
158 *      Set Info - output buffer length
159 *
160 * CreditCharge = ((RequestLength - 1) / (64 * 1024)) + 1;
161 * Dont forget to increment the MessageIDs by the same amount
162 */
163uint32_t
164smb2_rq_credit_check(struct smb_rq *rqp, uint32_t len)
165{
166    /* This function returns the allowable length based on avail credits */
167    int16_t credit_charge16;
168    int32_t credit_charge32;
169    int32_t curr_credits;
170    uint32_t ret_len = len;
171
172    /*
173     * VC Credit lock must be held before calling this function
174     */
175
176    /* Do simple checks first */
177    if (len <= (64 * 1024)) {
178        /* default of one credit is fine and length is fine as is */
179        return ret_len;
180    }
181
182    /* How many credits do we have? */
183    curr_credits = OSAddAtomic(0, &rqp->sr_vc->vc_credits_granted);
184
185    if (curr_credits <= kCREDIT_LOW_WATER) {
186        /*
187         * Running low on credits, so stay with just default of one credit.
188         * Adjust len to be just 64K to fit in one credit
189         */
190        ret_len = 64 * 1024;
191        return ret_len;
192    }
193
194    /* how many credits are needed? */
195    credit_charge16 = ((len - 1) / (64 * 1024)) + 1;
196    credit_charge32 = credit_charge16;
197
198    if ((credit_charge32 + kCREDIT_LOW_WATER) > curr_credits) {
199        /* Not enough credits left, so reduce len of request */
200        ret_len = (curr_credits - kCREDIT_LOW_WATER) * (64 * 1024);
201        credit_charge16 = curr_credits - kCREDIT_LOW_WATER;
202    }
203
204    /* Update credit charge */
205    rqp->sr_creditcharge = credit_charge16;
206
207    DBG_ASSERT(ret_len != 0);
208    return ret_len;
209}
210
211/*
212 * Decrement vc_credits_granted by number of credits charged in request
213 * See smb2_rq_credit_check() for longer description
214 */
215static int
216smb2_rq_credit_decrement(struct smb_rq *rqp, uint32_t *rq_len)
217{
218    int32_t curr_credits;
219    int16_t credit_charge;
220    struct timespec ts;
221    int ret;
222    int error = 0;
223    struct smb_vc *vcp;
224    uint32_t sleep_cnt, i;
225    uint64_t message_id_diff = 0;
226
227	if (rqp == NULL) {
228        SMBERROR("rqp is NULL\n");
229		return ENOMEM;
230    }
231
232	if (rqp->sr_vc == NULL) {
233        SMBERROR("rqp->sr_vc is NULL\n");
234		return ENOMEM;
235    }
236    vcp = rqp->sr_vc;
237
238    SMBC_CREDIT_LOCK(vcp);
239
240    switch (rqp->sr_command) {
241        case SMB2_NEGOTIATE:
242            /*
243             * Negotiate is a special case where credit charge and credits
244             * requested are both 0. Its expected server will grant at least 1
245             * credit in Neg rsp
246             */
247            rqp->sr_creditcharge = 0;
248            rqp->sr_creditsrequested = 0;
249
250            /* Reset starting credits to be 0 in case of reconnect */
251            vcp->vc_credits_granted = 0;
252            vcp->vc_credits_ss_granted = 0;
253            vcp->vc_credits_max = 0;
254
255            goto out;
256
257        case SMB2_SESSION_SETUP:
258            /*
259             * We need more than a minimum of 3 credits in order to send
260             * at least one compound request instead of hanging waiting for
261             * more credits.
262             */
263            rqp->sr_creditsrequested = kCREDIT_REQUEST_AMT;
264            goto out;
265
266        case SMB2_TREE_CONNECT:
267        case SMB2_LOGOFF:
268            /*
269             * These commands are used during reconnect and we assume that
270             * we always have one credit to use.
271             */
272            goto out;
273
274        default:
275            if (!(vcp->vc_flags & SMBV_SMB2)) {
276                /*
277                 * Huh? Why are we trying to send SMB 2.x request on SMB 1.x
278                 * connection. This is not allowed. Need to find the code path
279                 * that got to here and fix it.
280                 */
281                SMBERROR("SMB 2.x not allowed on SMB 1.x connection. cmd = %x\n",
282                         rqp->sr_command);
283                error = ERPCMISMATCH;
284                goto out;
285            }
286
287            if (rqp->sr_context == vcp->vc_iod->iod_context) {
288                /*
289                 * More reconnect commands, assume that we always have one
290                 * credit to use
291                 */
292                goto out;
293            }
294
295            /* All other requests must go through regular crediting code */
296            break;
297    }
298
299    /*
300     * Check to see if need to pause sending until we get more credits
301     * Two ways to run out of credits.
302     * 1) Just have no credits left
303     * 2) (curr message ID) - (oldest pending message ID) > current credits
304     */
305 	for (;;) {
306        curr_credits = OSAddAtomic(0, &vcp->vc_credits_granted);
307        if (curr_credits >= kCREDIT_MIN_AMT) {
308            if (vcp->vc_req_pending == 0) {
309                /* Have enough credits and no pending reqs, so go send it */
310            break;
311        }
312
313            /* Have a pending request, see if send window is open */
314            if (vcp->vc_message_id > vcp->vc_oldest_message_id) {
315                message_id_diff = vcp->vc_message_id - vcp->vc_oldest_message_id;
316            }
317            else {
318                /* Must have wrapped around */
319                message_id_diff = UINT64_MAX - vcp->vc_oldest_message_id;
320                message_id_diff += vcp->vc_message_id;
321            }
322
323            if (message_id_diff <= (uint64_t) curr_credits) {
324                /* Send window still open, so go send it */
325                break;
326            }
327        }
328
329        if (rqp->sr_command == SMB2_ECHO) {
330            /*
331             * Can not block waiting here for credits for an Echo request.
332             * Echo request is sent by smb_iod_sendall() and that is the same
333             * function that times out requests that have not gotten their
334             * replies. Just skip sending the Echo request and return an error.
335             */
336            SMBC_CREDIT_UNLOCK(vcp);
337            return ENOBUFS;
338        }
339
340        /* If the share is going away, just return immediately */
341        if ((rqp->sr_share) &&
342            (rqp->sr_share->ss_going_away) &&
343            (rqp->sr_share->ss_going_away(rqp->sr_share))) {
344            SMBC_CREDIT_UNLOCK(vcp);
345            return ENXIO;
346        }
347
348        /* Block until we get more credits */
349        SMBDEBUG("Wait for credits curr %d max %d curr ID %lld pending ID %lld vc_credits_wait %d\n",
350                 curr_credits, vcp->vc_credits_max,
351                 vcp->vc_message_id, vcp->vc_oldest_message_id,
352                 vcp->vc_credits_wait);
353
354        /* Only wait a max of 60 seconds waiting for credits */
355        sleep_cnt = 60;
356        ts.tv_sec = 1;
357        ts.tv_nsec = 0;
358
359        for (i = 1; i <= sleep_cnt; i++) {
360            /* Indicate that we are sleeping by incrementing wait counter */
361            OSAddAtomic(1, &vcp->vc_credits_wait);
362
363            ret = msleep(&vcp->vc_credits_wait, SMBC_CREDIT_LOCKPTR(vcp),
364                         PWAIT, "vc-credits-wait", &ts);
365
366            if (ret == EWOULDBLOCK) {
367                /* Timed out, so decrement wait counter */
368                 OSAddAtomic(-1, &vcp->vc_credits_wait);
369
370                /* If the share is going away, just return immediately */
371                if ((rqp->sr_share) &&
372                    (rqp->sr_share->ss_going_away) &&
373                    (rqp->sr_share->ss_going_away(rqp->sr_share))) {
374                    SMBC_CREDIT_UNLOCK(vcp);
375                    return ENXIO;
376                }
377
378                /* Loop again and wait some more */
379            }
380            else {
381                /*
382                 * If we are woken up and its not EWOULDBLOCK, then must
383                 * have credits now, go and check.
384                 */
385                break;
386            }
387        }
388
389        if (ret == EWOULDBLOCK) {
390            /*
391             * Something really went wrong here. We should not have to wait
392             * this long to get any credits. Force a reconnect.
393             */
394            SMBERROR("Timed out waiting for credits %d \n", ret);
395
396            /* Reconnect requests will need the credit lock so free it */
397            SMBC_CREDIT_UNLOCK(vcp);
398
399            (void) smb_vc_force_reconnect(vcp);
400
401            /* Reconnect is done now, reacquire credit lock and try again */
402            SMBC_CREDIT_LOCK(vcp);
403        }
404	}
405
406    if (rq_len != NULL) {
407        /* Possible multi credit request */
408        *rq_len = smb2_rq_credit_check(rqp, *rq_len);
409    }
410
411    credit_charge = rqp->sr_creditcharge;
412
413    /* Decrement number of credits we have left */
414    OSAddAtomic(-(credit_charge), &vcp->vc_credits_granted);
415
416    /* Message IDs are set right before the request is sent */
417
418    /*
419     * Check to see if need to request more credits.
420     */
421    curr_credits = OSAddAtomic(0, &vcp->vc_credits_granted);
422
423    if (curr_credits < kCREDIT_MAX_AMT) {
424        /*
425         * Currently the client keeps trying to get more and more credits
426         * until get kCREDIT_MAX_AMT. Essentially its up to the server to
427         * throttle back our client.
428         * Optionally, we could change kCREDIT_MAX_AMT to be kCREDIT_LOW_WATER
429         * so that we only request more credits when we need them.
430         */
431        rqp->sr_creditsrequested = kCREDIT_REQUEST_AMT;
432
433        /* Dont access sr_creditreqp as its not set up yet */
434    }
435
436    if (curr_credits < 0) {
437        SMBERROR("credit count %d < 0 \n", curr_credits);
438    }
439
440out:
441    SMBC_CREDIT_UNLOCK(vcp);
442
443    return error;
444}
445
446/*
447 * Increment vc_credits_granted by number of credits granted by server
448 * See smb2_rq_credit_check() for longer description
449 */
450int
451smb2_rq_credit_increment(struct smb_rq *rqp)
452{
453    int32_t curr_credits;
454    struct smb_vc *vcp;
455
456	if (rqp == NULL) {
457        SMBERROR("rqp is NULL\n");
458		return (ENOMEM);
459    }
460
461	if (rqp->sr_vc == NULL) {
462        SMBERROR("rqp->sr_vc is NULL in %lld \n", rqp->sr_messageid);
463		return (ENOMEM);
464    }
465    vcp = rqp->sr_vc;
466
467    if (rqp->sr_rspcreditsgranted == 0) {
468        /* Nothing to do */
469        return (0);
470    }
471
472    switch (rqp->sr_command) {
473        case SMB2_NEGOTIATE:
474        case SMB2_TREE_CONNECT:
475        case SMB2_LOGOFF:
476            /*
477             * These commands are used during reconnect and we assume
478             * they are always granted one credit back.
479             */
480            return (0);
481
482        case SMB2_SESSION_SETUP:
483            /*
484             * Save credits granted from Session Setup in separate variable.
485             * Once we are ready to start tracking credits then we will set
486             * this number as our starting number of credits.
487             */
488            SMBC_CREDIT_LOCK(vcp);
489            OSAddAtomic(rqp->sr_rspcreditsgranted, &vcp->vc_credits_ss_granted);
490            SMBC_CREDIT_UNLOCK(vcp);
491            return (0);
492
493        default:
494            if (rqp->sr_context == vcp->vc_iod->iod_context) {
495                /*
496                 * More reconnect commands, assume that we always are granted
497                 * one credit back
498                 */
499                return (0);
500            }
501
502            /* All other responses must go through regular crediting code */
503            break;
504    }
505
506    SMBC_CREDIT_LOCK(vcp);
507
508    OSAddAtomic(rqp->sr_rspcreditsgranted, &vcp->vc_credits_granted);
509
510    /* Keep track of max number of credits that server has granted us */
511    curr_credits = OSAddAtomic(0, &vcp->vc_credits_granted);
512    if (vcp->vc_credits_max < (uint32_t) curr_credits) {
513        vcp->vc_credits_max = curr_credits;
514
515        /* Set an upper limit to the number of credits that client can use */
516        if (vcp->vc_credits_max > kCREDIT_MAX_AMT) {
517            vcp->vc_credits_max = kCREDIT_MAX_AMT;
518        }
519    }
520
521	/* Wake up any requests waiting for more credits */
522    if (vcp->vc_credits_wait) {
523        OSAddAtomic(-1, &vcp->vc_credits_wait);
524        wakeup(&vcp->vc_credits_wait);
525	}
526
527    SMBC_CREDIT_UNLOCK(vcp);
528
529    return 0;
530}
531
532/*
533 * Set starting number of credits.
534 * For the Login/Reconnect commands of Negotiate, Session Setup, Tree Connect,
535 * and Log Off, we assume we always have 1 credit and that we always get 1
536 * credit back. The credit count is not incremented during this time.
537 * During a Login, this is fine as its a synchronous operation
538 * with just one request being done at a time. During Reconnect, this is
539 * important as you may have several threads waiting for credits when we go
540 * into reconnect. In Reconnect, the credit count goes back to 0. We dont want
541 * to increment our credit count on the replies to these commands as then the
542 * other threads may grab all the credits and then the reconnect commands would
543 * be stuck waiting for more credits that will not arrive. Once Reconnect is
544 * done, then we set the credit count to be the number of credits that we got
545 * back from the Session Setup replies.
546 *
547 * Each Session Setup request will ask for more credits. The credits granted
548 * in those replies are saved in a separate counter. When crediting is allowed
549 * to start up, smb2_rq_credit_start() is called.
550 * smb2_rq_credit_start() is called in two cases:
551 * 1) Not in reconnect, this is called after Session Setup is done
552 * 2) In reconnect, called after reconnect finishes
553 *
554 * If credits is 0, then set credits to the ones granted by the Session Setup
555 * responses.  If credits is non zero, then set it to that value (failed
556 * reconnect situation).
557 *
558 * The very next commands sent after Logging in or after Reconnect may be
559 * compound request, we need to start with at least > 3 credits.
560 */
561void
562smb2_rq_credit_start(struct smb_vc *vcp, uint16_t credits)
563{
564    DBG_ASSERT(vcp != NULL);
565
566    SMBC_CREDIT_LOCK(vcp);
567
568    if (credits == 0) {
569        /*
570         * Set our starting number of credits to the number granted to us from
571         * the Session Setup Responses.
572         */
573        OSAddAtomic(vcp->vc_credits_ss_granted, &vcp->vc_credits_granted);
574    }
575    else {
576        /* Set to passed in value. Should be due to failed reconnect */
577        OSAddAtomic(credits, &vcp->vc_credits_granted);
578    }
579
580    /* Set max credits to same value */
581    vcp->vc_credits_max = vcp->vc_credits_granted;
582
583    /* Clear out oldest message ID to open send window */
584    vcp->vc_req_pending = 0;
585    vcp->vc_oldest_message_id = 0;
586
587	/* Wake up any requests waiting for more credits */
588    if (vcp->vc_credits_wait) {
589        OSAddAtomic(-1, &vcp->vc_credits_wait);
590        wakeup(&vcp->vc_credits_wait);
591	}
592
593    SMBC_CREDIT_UNLOCK(vcp);
594}
595
596/*
597 * The object is either a share or a vc in either case the calling routine
598 * must have a reference on the object before calling this routine.
599 */
600static int
601smb2_rq_init_internal(struct smb_rq *rqp, struct smb_connobj *obj, u_char cmd,
602                      uint32_t *rq_len, int rq_flags, vfs_context_t context)
603{
604	int error;
605
606    /* Fill in the smb_rq */
607	bzero(rqp, sizeof(*rqp));
608	rqp->sr_flags |= rq_flags;
609	lck_mtx_init(&rqp->sr_slock, srs_lck_group, srs_lck_attr);
610	error = smb_rq_getenv(obj, &rqp->sr_vc, &rqp->sr_share);
611	if (error)
612		goto done;
613
614	error = smb_vc_access(rqp->sr_vc, context);
615	if (error)
616		goto done;
617
618	rqp->sr_command = cmd;
619    rqp->sr_creditcharge = 1;
620    rqp->sr_creditsrequested = 1;
621    rqp->sr_rqtreeid = 0;
622    rqp->sr_rqsessionid = rqp->sr_vc->vc_session_id;
623
624	rqp->sr_context = context;
625	rqp->sr_extflags |= SMB2_REQUEST;
626
627    /*
628     * Decrement current credit count
629     * ASSUMPTION - a built request will always get sent, otherwise the credit
630     * counts and message ids will get out of sync.
631     */
632    error = smb2_rq_credit_decrement(rqp, rq_len);
633    if (error) {
634        /*
635         * if got an error, then must not have used any credits and we did not
636         * send the request. Set sr_creditcharge to 0 so we do not try to
637         * recover any credits in smb_rq_done()
638         */
639        rqp->sr_creditcharge = 0;
640        goto done;
641    }
642
643    switch (rqp->sr_command) {
644        case SMB2_NEGOTIATE:
645        case SMB2_SESSION_SETUP:
646            break;
647
648        case SMB2_LOGOFF:
649        case SMB2_ECHO:
650        case SMB2_CANCEL:
651        case SMB2_TREE_CONNECT:
652            /* these cmds always have tree id of 0 */
653            break;
654
655        default:
656            /* If the request has a share then it has a reference on it */
657            rqp->sr_rqtreeid = htolel(rqp->sr_share ?
658                                      rqp->sr_share->ss_tree_id :
659                                      SMB2_TID_UNKNOWN);
660            break;
661    }
662
663    /* Create the SMB2 Header */
664    error = smb2_rq_new(rqp);
665done:
666	if (error) {
667		smb_rq_done(rqp);
668	}
669	return (error);
670}
671
672/*
673 * Returns uint32_t length of the SMB2 request
674 */
675uint32_t
676smb2_rq_length(struct smb_rq *rqp)
677{
678    uint32_t length;
679
680    length = (uint32_t) rqp->sr_rq.mb_len;
681    return (length);
682}
683
684/*
685 * Set Message ID in the request and increment vc_message_id by the number
686 * of credits used in the request.
687 */
688int
689smb2_rq_message_id_increment(struct smb_rq *rqp)
690{
691    int64_t message_id = 0;
692    struct smb_rq *tmp_rqp;
693
694	if (rqp == NULL) {
695        SMBERROR("rqp is NULL\n");
696		return ENOMEM;
697    }
698
699	if (rqp->sr_vc == NULL) {
700        SMBERROR("rqp->sr_vc is NULL\n");
701		return ENOMEM;
702    }
703
704    SMBC_CREDIT_LOCK(rqp->sr_vc);
705
706    if (rqp->sr_command == SMB2_NEGOTIATE) {
707        /*
708         * Negotiate is a special case where credit charge is 0.
709         * Increment message_id by 1.
710         */
711        message_id = OSAddAtomic64(1, (int64_t*) &rqp->sr_vc->vc_message_id);
712        rqp->sr_messageid = message_id;
713        *rqp->sr_messageidp = htoleq(rqp->sr_messageid);
714
715        goto out;
716    }
717
718    if (rqp->sr_flags & SMBR_COMPOUND_RQ) {
719        /*
720         * Compound Request
721         * Set first rqp's message_id
722         * Increment message_id by same amount as the credit charge
723         */
724        message_id = OSAddAtomic64(rqp->sr_creditcharge,
725                                   (int64_t*) &rqp->sr_vc->vc_message_id);
726        rqp->sr_messageid = message_id;
727        *rqp->sr_messageidp = htoleq(rqp->sr_messageid);
728
729        /* Set message_id in the other requests */
730        tmp_rqp = rqp->sr_next_rqp;
731        while (tmp_rqp != NULL) {
732            /* Increment message_id by same amount as the credit charge */
733            message_id = OSAddAtomic64(tmp_rqp->sr_creditcharge,
734                                       (int64_t*) &tmp_rqp->sr_vc->vc_message_id);
735            tmp_rqp->sr_messageid = message_id;
736            *tmp_rqp->sr_messageidp = htoleq(tmp_rqp->sr_messageid);
737
738            tmp_rqp = tmp_rqp->sr_next_rqp;
739        }
740    }
741    else {
742        /*
743         * Single Request
744         * Increment message_id by same amount as the credit charge
745         */
746        message_id = OSAddAtomic64(rqp->sr_creditcharge,
747                                   (int64_t*) &rqp->sr_vc->vc_message_id);
748        rqp->sr_messageid = message_id;
749        *rqp->sr_messageidp = htoleq(rqp->sr_messageid);
750    }
751
752out:
753    SMBC_CREDIT_UNLOCK(rqp->sr_vc);
754
755    return 0;
756}
757
758/*
759 * next_cmd_offset is the total of all next_cmd_offset offsets. Subtract the
760 * bytes that we have parsed and that should leave the number of pad bytes to
761 * consume.
762 */
763int
764smb2_rq_next_command(struct smb_rq *rqp, size_t *next_cmd_offset,
765                     struct mdchain *mdp)
766{
767    ssize_t pad_bytes = 0;
768    int error = 0;
769
770    *next_cmd_offset += rqp->sr_rspnextcmd;
771
772    /* take total of next_cmd_offset and subtract what we have parsed */
773    pad_bytes = *next_cmd_offset - mdp->md_len;
774
775    if (pad_bytes > 0) {
776        error = md_get_mem(mdp, NULL, pad_bytes, MB_MSYSTEM);
777        if (error) {
778            SMBERROR("md_get_mem failed %d\n", error);
779        }
780    }
781    return(error);
782}
783
784static int
785smb2_rq_new(struct smb_rq *rqp)
786{
787	struct mbchain *mbp;
788	struct mdchain *mdp;
789	int error;
790
791    smb_rq_getrequest(rqp, &mbp);
792    smb_rq_getreply(rqp, &mdp);
793
794	mb_done(mbp);
795	md_done(mdp);
796	error = mb_init(mbp);
797	if (error) {
798		return error;
799    }
800
801    if (!(rqp->sr_flags & SMBR_ASYNC)) {
802        /*
803         * Build SMB2 Sync Header
804         */
805        mb_put_mem(mbp, SMB2_SIGNATURE, SMB2_SIGLEN, MB_MSYSTEM); /* Protocol ID */
806        mb_put_uint16le(mbp, SMB2_HDRLEN);                      /* Struct Size */
807        rqp->sr_creditchargep = (uint16_t *)mb_reserve(mbp, 2); /* Credit Charge */
808        *rqp->sr_creditchargep = htoles(rqp->sr_creditcharge);
809        mb_put_uint32le(mbp, 0);                                /* Status */
810        mb_put_uint16le(mbp, rqp->sr_command);                  /* Command */
811        rqp->sr_creditreqp = (uint16_t *)mb_reserve(mbp, 2);    /* Credit Req/Rsp */
812        *rqp->sr_creditreqp = htoles(rqp->sr_creditsrequested);
813        rqp->sr_flagsp = (uint32_t *)mb_reserve(mbp, 4);        /* Flags */
814        *rqp->sr_flagsp = htolel(rqp->sr_rqflags);
815        rqp->sr_nextcmdp = (uint32_t *)mb_reserve(mbp, 4);      /* Next command */
816        *rqp->sr_nextcmdp = htolel(rqp->sr_nextcmd);
817        rqp->sr_messageidp = (uint64_t *)mb_reserve(mbp, 8);    /* Message ID */
818        bzero(rqp->sr_messageidp, 8);
819        mb_put_uint32le(mbp, 0xFEFF);                           /* Process ID */
820        mb_put_uint32le(mbp, rqp->sr_rqtreeid);                 /* Tree ID */
821        mb_put_uint64le(mbp, rqp->sr_rqsessionid);              /* Session ID */
822        rqp->sr_rqsig = (uint8_t *)mb_reserve(mbp, 16);         /* Signature */
823        bzero(rqp->sr_rqsig, 16);
824    }
825    else {
826        /*
827         * Build SMB2 Async Header
828         */
829        mb_put_mem(mbp, SMB2_SIGNATURE, SMB2_SIGLEN, MB_MSYSTEM); /* Protocol ID */
830        mb_put_uint16le(mbp, SMB2_HDRLEN);                      /* Struct Size */
831        rqp->sr_creditchargep = (uint16_t *)mb_reserve(mbp, 2); /* Credit Charge */
832        *rqp->sr_creditchargep = htoles(rqp->sr_creditcharge);
833        mb_put_uint32le(mbp, 0);                                /* Status */
834        mb_put_uint16le(mbp, rqp->sr_command);                  /* Command */
835        rqp->sr_creditreqp = (uint16_t *)mb_reserve(mbp, 2);    /* Credit Req/Rsp */
836        *rqp->sr_creditreqp = htoles(rqp->sr_creditsrequested);
837        rqp->sr_flagsp = (uint32_t *)mb_reserve(mbp, 4);        /* Flags */
838        rqp->sr_rqflags |= SMB2_FLAGS_ASYNC_COMMAND;
839        *rqp->sr_flagsp = htolel(rqp->sr_rqflags);
840        rqp->sr_nextcmdp = (uint32_t *)mb_reserve(mbp, 4);      /* Next command */
841        *rqp->sr_nextcmdp = htolel(rqp->sr_nextcmd);
842        rqp->sr_messageidp = (uint64_t *)mb_reserve(mbp, 8);    /* Message ID */
843        bzero(rqp->sr_messageidp, 8);
844        mb_put_uint64le(mbp, 0);                                /* Async ID */
845        mb_put_uint64le(mbp, rqp->sr_rqsessionid);              /* Session ID */
846        rqp->sr_rqsig = (uint8_t *)mb_reserve(mbp, 16);         /* Signature */
847        bzero(rqp->sr_rqsig, 16);
848    }
849
850	return 0;
851}
852
853/*
854 * Parses SMB2 Response Header
855 * For non compound responses, the mdp is from the rqp.
856 * For compound responses, the mdp may be from the first rqp in the chain and
857 * not from the rqp passed into this function.
858 */
859uint32_t
860smb2_rq_parse_header(struct smb_rq *rqp, struct mdchain **mdp)
861{
862	int error = 0, rperror = 0;
863    uint32_t protocol_id;
864	uint16_t length, credit_charge, command;
865    uint64_t message_id = 0;
866    uint64_t async_id = 0;
867    struct mdchain md_sign;
868    uint8_t signature[16];
869
870    /*
871     * Parse SMB2 Header
872     * We are already pointing to begining of header data
873     */
874
875	if (rqp == NULL) {
876        SMBERROR("rqp is NULL\n");
877		error = ENOMEM;
878        goto bad;
879    }
880
881    /*
882     * <14227703> If SMB2_RESPONSE is set, then the reply is in the rqp
883     * Must be a server that does not support compound replies
884     */
885    if (rqp->sr_extflags & SMB2_RESPONSE) {
886        /* Get pointer to response data */
887        smb_rq_getreply(rqp, mdp);
888    }
889
890    /*
891     * Hold on to a copy of the mdchain before we touch it,
892     * since we will need to verify the signature
893     * if signing is turned on
894     */
895    md_sign = **mdp;
896
897    /* Get Protocol ID */
898    error = md_get_uint32le(*mdp, &protocol_id);
899    if (error) {
900        goto bad;
901    }
902
903    /* Check structure size is 64 */
904    error = md_get_uint16le(*mdp, &length);
905    if (error) {
906        goto bad;
907    }
908    if (length != 64) {
909        SMBERROR("Bad struct size: %u\n", (uint32_t) length);
910        error = EBADRPC;
911        goto bad;
912    }
913
914    /* Get Credit Charge */
915    error = md_get_uint16le(*mdp, &credit_charge);
916    if (error) {
917        goto bad;
918    }
919
920    /* Get Status */
921    error = md_get_uint32le(*mdp, &rqp->sr_ntstatus);
922    if (error) {
923        goto bad;
924    }
925
926    /* Get Command */
927    error = md_get_uint16le(*mdp, &command);
928    if (error) {
929        goto bad;
930    }
931
932    /* Get Credits Granted */
933    error = md_get_uint16le(*mdp, &rqp->sr_rspcreditsgranted);
934    if (error) {
935        goto bad;
936    }
937
938    /* Increment current credits granted */
939    smb2_rq_credit_increment(rqp);
940
941    /* Get Flags */
942    error = md_get_uint32le(*mdp, &rqp->sr_rspflags);
943    if (error) {
944        goto bad;
945    }
946
947    /* Get Next Command offset */
948    error = md_get_uint32le(*mdp, &rqp->sr_rspnextcmd);
949    if (error) {
950        goto bad;
951    }
952
953    /* Get Message ID */
954    error = md_get_uint64le(*mdp, &message_id);
955    if (error) {
956        goto bad;
957    }
958
959    if (!(rqp->sr_rspflags & SMB2_FLAGS_ASYNC_COMMAND)) {
960        /*
961         * Sync Header
962         */
963
964        /* Get Process ID */
965        error = md_get_uint32le(*mdp, &rqp->sr_rsppid);
966        if (error) {
967            goto bad;
968        }
969
970        /* Get Tree ID */
971        error = md_get_uint32le(*mdp, &rqp->sr_rsptreeid);
972        if (error) {
973            goto bad;
974        }
975    }
976    else {
977        /*
978         * Async Header
979         */
980
981        /* Get Async ID */
982        error = md_get_uint64le(*mdp, &async_id);
983        if (error) {
984            goto bad;
985        }
986
987        if (async_id != rqp->sr_rspasyncid) {
988            SMBERROR("Async rsp ids do not match: id %lld async_id %lld ! = %lld\n",
989                     message_id, async_id, rqp->sr_rspasyncid);
990            error = EBADRPC;
991            goto bad;
992        }
993    }
994
995    /* Get Session ID */
996    error = md_get_uint64le(*mdp, &rqp->sr_rspsessionid);
997    if (error) {
998        goto bad;
999    }
1000
1001    /* Get Signature */
1002	error = md_get_mem(*mdp, (caddr_t) &signature, sizeof(signature), MB_MSYSTEM);
1003    if (error) {
1004        goto bad;
1005    }
1006
1007    /* If it's signed, then verify the signature */
1008    if ( (error == 0) &&
1009        (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)) {
1010        error = smb2_rq_verify(rqp, &md_sign, signature);
1011    }
1012
1013    /*
1014     * If the signature failed verification, do not bother with
1015     * looking at sr_ntstatus, just punt.
1016     */
1017    if (error)
1018        goto bad;
1019
1020    /* Convert NT Status to an errno value */
1021    rperror = smb_ntstatus_to_errno(rqp->sr_ntstatus);
1022
1023    switch (rqp->sr_ntstatus) {
1024        case STATUS_INSUFFICIENT_RESOURCES:
1025            SMBWARNING("STATUS_INSUFFICIENT_RESOURCES: while attempting cmd %x\n",
1026                       rqp->sr_cmd);
1027            break;
1028
1029        case STATUS_NETWORK_SESSION_EXPIRED:
1030            if (rqp->sr_context == rqp->sr_vc->vc_iod->iod_context) {
1031                /*
1032                 * Its a reconnect command so dont recurse into reconnect
1033                 * again.  Just fail reconnect.
1034                 */
1035                SMBWARNING("STATUS_NETWORK_SESSION_EXPIRED: while reconnecting cmd %x. Disconnecting.\n",
1036                         rqp->sr_cmd);
1037                rperror = ENETRESET;
1038            }
1039            else {
1040                SMBWARNING("STATUS_NETWORK_SESSION_EXPIRED: while attempting cmd %x. Reconnecting.\n",
1041                         rqp->sr_cmd);
1042
1043               (void) smb_vc_force_reconnect(rqp->sr_vc);
1044            }
1045            break;
1046
1047        default:
1048            break;
1049    }
1050
1051    /* The tree has gone away, umount the volume. */
1052	if ((rperror == ENETRESET) && rqp->sr_share) {
1053		lck_mtx_lock(&rqp->sr_share->ss_shlock);
1054		if ( rqp->sr_share->ss_dead)
1055			rqp->sr_share->ss_dead(rqp->sr_share);
1056		lck_mtx_unlock(&rqp->sr_share->ss_shlock);
1057	}
1058
1059    /* Need bigger buffer? */
1060	if (rperror && (rqp->sr_ntstatus == STATUS_BUFFER_TOO_SMALL)) {
1061		rqp->sr_flags |= SMBR_MOREDATA;
1062	} else {
1063		rqp->sr_flags &= ~SMBR_MOREDATA;
1064	}
1065
1066bad:
1067	return error ? error : rperror;
1068}
1069
1070/*
1071 * Sets SMB2 Header Flags and NextCommand for Compound req chains
1072 */
1073int
1074smb2_rq_update_cmpd_hdr(struct smb_rq *rqp, uint32_t position_flag)
1075{
1076	if (rqp == NULL)
1077		return ENOMEM;
1078
1079	if (rqp->sr_nextcmdp == NULL)
1080		return ENOMEM;
1081
1082    if (rqp->sr_flagsp == NULL)
1083        return ENOMEM;
1084
1085    switch (position_flag) {
1086        case SMB2_CMPD_FIRST:
1087            /*
1088             * Do not set SMB2_FLAGS_RELATED_OPERATIONS
1089             * Set next command offset
1090             */
1091            *rqp->sr_nextcmdp = htolel(smb2_rq_length(rqp));
1092            break;
1093        case SMB2_CMPD_MIDDLE:
1094            /*
1095             * Set SMB2_FLAGS_RELATED_OPERATIONS
1096             * Set next command offset
1097             */
1098            *rqp->sr_nextcmdp = htolel(smb2_rq_length(rqp));
1099            *rqp->sr_flagsp |= htolel(SMB2_FLAGS_RELATED_OPERATIONS);
1100            break;
1101        case SMB2_CMPD_LAST:
1102            /*
1103             * Set SMB2_FLAGS_RELATED_OPERATIONS
1104             * Set next command offset to be 0
1105             */
1106            *rqp->sr_nextcmdp = htolel(0);
1107            *rqp->sr_flagsp |= htolel(SMB2_FLAGS_RELATED_OPERATIONS);
1108            break;
1109
1110        default:
1111            SMBERROR("Unknown postion_flag %d\n", position_flag);
1112            return ENOMEM;
1113            break;
1114    }
1115
1116    return 0;
1117}
1118
1119
1120
1121
1122
1123