]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/netncp/ncp_ncp.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / netncp / ncp_ncp.c
1 /*-
2  * Copyright (c) 1999, 2000, 2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * Core of NCP protocol
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/errno.h>
40 #include <sys/systm.h>
41 #include <sys/proc.h>
42 #include <sys/signalvar.h>
43 #include <sys/sysctl.h>
44 #include <sys/mbuf.h>
45 #include <sys/lock.h>
46 #include <sys/mutex.h>
47 #include <sys/uio.h>
48
49 #include <netipx/ipx.h>
50 #include <netipx/ipx_var.h>
51
52 #include <netncp/ncp.h>
53 #include <netncp/ncp_conn.h>
54 #include <netncp/ncp_sock.h>
55 #include <netncp/ncp_subr.h>
56 #include <netncp/ncp_ncp.h>
57 #include <netncp/ncp_rq.h>
58 #include <netncp/nwerror.h>
59
60 #ifdef NCP_DATA_DEBUG
61 static
62 void m_dumpm(struct mbuf *m) {
63         char *p;
64         int len;
65         printf("d=");
66         while(m) {
67                 p=mtod(m,char *);
68                 len=m->m_len;
69                 printf("(%d)",len);
70                 while(len--){
71                         printf("%02x ",((int)*(p++)) & 0xff);
72                 }
73                 m=m->m_next;
74         };
75         printf("\n");
76 }
77 #endif /* NCP_DATA_DEBUG */
78
79 int
80 ncp_chkintr(struct ncp_conn *conn, struct thread *td)
81 {
82         struct proc *p;
83         sigset_t tmpset;
84
85         if (td == NULL)
86                 return 0;
87         p = td->td_proc;
88         PROC_LOCK(p);
89         tmpset = p->p_siglist;
90         SIGSETOR(tmpset, td->td_siglist);
91         SIGSETNAND(tmpset, td->td_sigmask);
92         mtx_lock(&p->p_sigacts->ps_mtx);
93         SIGSETNAND(tmpset, p->p_sigacts->ps_sigignore);
94         mtx_unlock(&p->p_sigacts->ps_mtx);
95         if (SIGNOTEMPTY(td->td_siglist) && NCP_SIGMASK(tmpset)) {
96                 PROC_UNLOCK(p);
97                 return EINTR;
98         }
99         PROC_UNLOCK(p);
100         return 0;
101 }
102
103 /*
104  * Process initial NCP handshake (attach)
105  * NOTE: Since all functions below may change conn attributes, they
106  * should be called with LOCKED connection, also they use procp & ucred
107  */
108 int
109 ncp_ncp_connect(struct ncp_conn *conn)
110 {
111         struct ncp_rq *rqp;
112         struct ncp_rphdr *rp;
113         int error;
114
115         error = ncp_rq_alloc_any(NCP_ALLOC_SLOT, 0, conn, conn->td, conn->ucred, &rqp);
116         if (error)
117                 return error;
118
119         conn->flags &= ~(NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED |
120             NCPFL_ATTACHED | NCPFL_LOGGED | NCPFL_INVALID);
121         conn->seq = 0;
122         error = ncp_request_int(rqp);
123         if (!error) {
124                 rp = mtod(rqp->rp.md_top, struct ncp_rphdr*);
125                 conn->connid = rp->conn_low + (rp->conn_high << 8);
126         }
127         ncp_rq_done(rqp);
128         if (error)
129                 return error;
130         conn->flags |= NCPFL_ATTACHED | NCPFL_WASATTACHED;
131         return 0;
132 }
133
134 int
135 ncp_ncp_disconnect(struct ncp_conn *conn)
136 {
137         struct ncp_rq *rqp;
138         int error;
139
140         NCPSDEBUG("for connid=%d\n",conn->nc_id);
141 #ifdef NCPBURST
142         ncp_burst_disconnect(conn);
143 #endif
144         if (conn->flags & NCPFL_ATTACHED) {
145                 error = ncp_rq_alloc_any(NCP_FREE_SLOT, 0, conn, conn->td, conn->ucred, &rqp);
146                 if (!error) {
147                         ncp_request_int(rqp);
148                         ncp_rq_done(rqp);
149                 }
150         }
151         ncp_conn_invalidate(conn);
152         ncp_sock_disconnect(conn);
153         return 0;
154 }
155
156 /*
157  * All negotiation functions expect a locked connection
158  */
159
160 int
161 ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target)
162 {
163         struct ncp_rq *rqp;
164         u_int16_t bsize;
165         int error;
166
167         error = ncp_rq_alloc(0x21, conn, conn->td, conn->ucred, &rqp);
168         if (error)
169                 return error;
170         mb_put_uint16be(&rqp->rq, size);
171         error = ncp_request(rqp);
172         if (error)
173                 return error;
174         md_get_uint16be(&rqp->rp, &bsize);
175         *target = min(bsize, size);
176         ncp_rq_done(rqp);
177         return error;
178 }
179
180 static int
181 ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options,
182             int *ret_size, u_int8_t *ret_options)
183 {
184         struct ncp_rq *rqp;
185         u_int16_t rs;
186         int error;
187
188         error = ncp_rq_alloc(0x61, conn, conn->td, conn->ucred, &rqp);
189         if (error)
190                 return error;
191         mb_put_uint16be(&rqp->rq, size);
192         mb_put_uint8(&rqp->rq, options);
193         rqp->nr_minrplen = 2 + 2 + 1;
194         error = ncp_request(rqp);
195         if (error)
196                 return error;
197         md_get_uint16be(&rqp->rp, &rs);
198         *ret_size = (rs == 0) ? size : min(rs, size);
199         md_get_uint16be(&rqp->rp, &rs);         /* skip echo socket */
200         md_get_uint8(&rqp->rp, ret_options);
201         ncp_rq_done(rqp);
202         return error;
203 }
204
205 int
206 ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, u_int8_t in_options)
207 {
208         u_int8_t options;
209         int neg_buffsize, error, sl, ckslevel;
210         size_t ilen;
211
212         sl = conn->li.sig_level;
213         if (sl >= 2)
214                 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
215         if (conn->li.saddr.sa_family == AF_IPX) {
216                 ilen = sizeof(ckslevel);
217                 error = kernel_sysctlbyname(curthread, "net.ipx.ipx.checksum",
218                     &ckslevel, &ilen, NULL, 0, NULL, 0);
219                 if (error)
220                         return error;
221                 if (ckslevel == 2)
222                         in_options |= NCP_IPX_CHECKSUM;
223         }
224         error = ncp_negotiate_size_and_options(conn, buffsize, in_options,
225             &neg_buffsize, &options);
226         if (!error) {
227                 if (conn->li.saddr.sa_family == AF_IPX &&
228                     ((options ^ in_options) & NCP_IPX_CHECKSUM)) {
229                         if (ckslevel == 2) {
230                                 printf("Server refuses to support IPX checksums\n");
231                                 return NWE_REQUESTER_FAILURE;
232                         }
233                         in_options |= NCP_IPX_CHECKSUM;
234                         error = 1;
235                 }
236                 if ((options ^ in_options) & 2) {
237                         if (sl == 0 || sl == 3)
238                                 return NWE_SIGNATURE_LEVEL_CONFLICT;
239                         if (sl == 1) {
240                                 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
241                                 error = 1;
242                         }
243                 }
244                 if (error) {
245                         error = ncp_negotiate_size_and_options(conn,
246                             buffsize, in_options, &neg_buffsize, &options);
247                         if ((options ^ in_options) & 3) {
248                                 return NWE_SIGNATURE_LEVEL_CONFLICT;
249                         }
250                 }
251         } else {
252                 in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
253                 error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
254                               &neg_buffsize);
255         }                         
256         if (error) return error;
257         if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
258                 return EINVAL;
259         conn->buffer_size = neg_buffsize;
260         if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
261                 conn->flags |= NCPFL_SIGNWANTED;
262         if (conn->li.saddr.sa_family == AF_IPX)
263                 ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
264         return 0;
265 }
266
267 void
268 ncp_check_rq(struct ncp_conn *conn)
269 {
270         return;
271         if (conn->flags & NCPFL_INTR)
272                 return;
273         /* first, check for signals */
274         if (ncp_chkintr(conn, conn->td))
275                 conn->flags |= NCPFL_INTR;
276         return;
277 }
278
279 int
280 ncp_get_bindery_object_id(struct ncp_conn *conn,
281                 u_int16_t object_type, char *object_name,
282                 struct ncp_bindery_object *target,
283                 struct thread *td, struct ucred *cred)
284 {
285         struct ncp_rq *rqp;
286         int error;
287
288         error = ncp_rq_alloc_subfn(23, 53, conn, conn->td, conn->ucred, &rqp);
289         mb_put_uint16be(&rqp->rq, object_type);
290         ncp_rq_pstring(rqp, object_name);
291         rqp->nr_minrplen = 54;
292         error = ncp_request(rqp);
293         if (error)
294                 return error;
295         md_get_uint32be(&rqp->rp, &target->object_id);
296         md_get_uint16be(&rqp->rp, &target->object_type);
297         md_get_mem(&rqp->rp, (caddr_t)target->object_name, 48, MB_MSYSTEM);
298         ncp_rq_done(rqp);
299         return 0;
300 }
301
302 /*
303  * target is a 8-byte buffer
304  */
305 int
306 ncp_get_encryption_key(struct ncp_conn *conn, char *target)
307 {
308         struct ncp_rq *rqp;
309         int error;
310
311         error = ncp_rq_alloc_subfn(23, 23, conn, conn->td, conn->ucred, &rqp);
312         if (error)
313                 return error;
314         rqp->nr_minrplen = 8;
315         error = ncp_request(rqp);
316         if (error)
317                 return error;
318         md_get_mem(&rqp->rp, target, 8, MB_MSYSTEM);
319         ncp_rq_done(rqp);
320         return error;
321 }
322
323 /*
324  * Initialize packet signatures. They a slightly modified MD4.
325  * The first 16 bytes of logindata are the shuffled password,
326  * the last 8 bytes the encryption key as received from the server.
327  */
328 static int
329 ncp_sign_start(struct ncp_conn *conn, char *logindata)
330 {
331         char msg[64];
332         u_int32_t state[4];
333
334         memcpy(msg, logindata, 24);
335         memcpy(msg + 24, "Authorized NetWare Client", 25);
336         bzero(msg + 24 + 25, sizeof(msg) - 24 - 25);
337
338         conn->sign_state[0] = 0x67452301;
339         conn->sign_state[1] = 0xefcdab89;
340         conn->sign_state[2] = 0x98badcfe;
341         conn->sign_state[3] = 0x10325476;
342         ncp_sign(conn->sign_state, msg, state);
343         conn->sign_root[0] = state[0];
344         conn->sign_root[1] = state[1];
345         conn->flags |= NCPFL_SIGNACTIVE;
346         return 0;
347 }
348
349
350 int
351 ncp_login_encrypted(struct ncp_conn *conn, struct ncp_bindery_object *object,
352         const u_char *key, const u_char *passwd,
353         struct thread *td, struct ucred *cred)
354 {
355         struct ncp_rq *rqp;
356         struct mbchain *mbp;
357         u_int32_t tmpID = htonl(object->object_id);
358         u_char buf[16 + 8];
359         u_char encrypted[8];
360         int error;
361
362         nw_keyhash((u_char*)&tmpID, passwd, strlen(passwd), buf);
363         nw_encrypt(key, buf, encrypted);
364
365         error = ncp_rq_alloc_subfn(23, 24, conn, td, cred, &rqp);
366         if (error)
367                 return error;
368         mbp = &rqp->rq;
369         mb_put_mem(mbp, encrypted, 8, MB_MSYSTEM);
370         mb_put_uint16be(mbp, object->object_type);
371         ncp_rq_pstring(rqp, object->object_name);
372         error = ncp_request(rqp);
373         if (!error)
374                 ncp_rq_done(rqp);
375         if ((conn->flags & NCPFL_SIGNWANTED) &&
376             (error == 0 || error == NWE_PASSWORD_EXPIRED)) {
377                 bcopy(key, buf + 16, 8);
378                 error = ncp_sign_start(conn, buf);
379         }
380         return error;
381 }
382
383 int
384 ncp_login_unencrypted(struct ncp_conn *conn, u_int16_t object_type,
385         const char *object_name, const u_char *passwd,
386         struct thread *td, struct ucred *cred)
387 {
388         struct ncp_rq *rqp;
389         int error;
390
391         error = ncp_rq_alloc_subfn(23, 20, conn, td, cred, &rqp);
392         if (error)
393                 return error;
394         mb_put_uint16be(&rqp->rq, object_type);
395         ncp_rq_pstring(rqp, object_name);
396         ncp_rq_pstring(rqp, passwd);
397         error = ncp_request(rqp);
398         if (!error)
399                 ncp_rq_done(rqp);
400         return error;
401 }
402
403 int
404 ncp_read(struct ncp_conn *conn, ncp_fh *file, struct uio *uiop, struct ucred *cred)
405 {
406         struct ncp_rq *rqp;
407         struct mbchain *mbp;
408         u_int16_t retlen = 0 ;
409         int error = 0, len = 0, tsiz, burstio;
410
411         tsiz = uiop->uio_resid;
412 #ifdef NCPBURST
413         burstio = (ncp_burst_enabled && tsiz > conn->buffer_size);
414 #else
415         burstio = 0;
416 #endif
417
418         while (tsiz > 0) {
419                 if (!burstio) {
420                         len = min(4096 - (uiop->uio_offset % 4096), tsiz);
421                         len = min(len, conn->buffer_size);
422                         error = ncp_rq_alloc(72, conn, uiop->uio_td, cred, &rqp);
423                         if (error)
424                                 break;
425                         mbp = &rqp->rq;
426                         mb_put_uint8(mbp, 0);
427                         mb_put_mem(mbp, (caddr_t)file, 6, MB_MSYSTEM);
428                         mb_put_uint32be(mbp, uiop->uio_offset);
429                         mb_put_uint16be(mbp, len);
430                         rqp->nr_minrplen = 2;
431                         error = ncp_request(rqp);
432                         if (error)
433                                 break;
434                         md_get_uint16be(&rqp->rp, &retlen);
435                         if (uiop->uio_offset & 1)
436                                 md_get_mem(&rqp->rp, NULL, 1, MB_MSYSTEM);
437                         error = md_get_uio(&rqp->rp, uiop, retlen);
438                         ncp_rq_done(rqp);
439                 } else {
440 #ifdef NCPBURST
441                         error = ncp_burst_read(conn, file, tsiz, &len, &retlen, uiop, cred);
442 #endif
443                 }
444                 if (error)
445                         break;
446                 tsiz -= retlen;
447                 if (retlen < len)
448                         break;
449         }
450         return (error);
451 }
452
453 int
454 ncp_write(struct ncp_conn *conn, ncp_fh *file, struct uio *uiop, struct ucred *cred)
455 {
456         struct ncp_rq *rqp;
457         struct mbchain *mbp;
458         int error = 0, len, tsiz, backup;
459
460         if (uiop->uio_iovcnt != 1) {
461                 printf("%s: can't handle iovcnt>1 !!!\n", __func__);
462                 return EIO;
463         }
464         tsiz = uiop->uio_resid;
465         while (tsiz > 0) {
466                 len = min(4096 - (uiop->uio_offset % 4096), tsiz);
467                 len = min(len, conn->buffer_size);
468                 if (len == 0) {
469                         printf("gotcha!\n");
470                 }
471                 /* rq head */
472                 error = ncp_rq_alloc(73, conn, uiop->uio_td, cred, &rqp);
473                 if (error)
474                         break;
475                 mbp = &rqp->rq;
476                 mb_put_uint8(mbp, 0);
477                 mb_put_mem(mbp, (caddr_t)file, 6, MB_MSYSTEM);
478                 mb_put_uint32be(mbp, uiop->uio_offset);
479                 mb_put_uint16be(mbp, len);
480                 error = mb_put_uio(mbp, uiop, len);
481                 if (error) {
482                         ncp_rq_done(rqp);
483                         break;
484                 }
485                 error = ncp_request(rqp);
486                 if (!error)
487                         ncp_rq_done(rqp);
488                 if (len == 0)
489                         break;
490                 if (error) {
491                         backup = len;
492                         uiop->uio_iov->iov_base =
493                             (char *)uiop->uio_iov->iov_base - backup;
494                         uiop->uio_iov->iov_len += backup;
495                         uiop->uio_offset -= backup;
496                         uiop->uio_resid += backup;
497                         break;
498                 }
499                 tsiz -= len;
500         }
501         if (error)
502                 uiop->uio_resid = tsiz;
503         switch (error) {
504             case NWE_INSUFFICIENT_SPACE:
505                 error = ENOSPC;
506                 break;
507         }
508         return (error);
509 }