xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: nslcd-prot.h,v 1.2 2021/08/14 16:14:52 christos Exp $	*/
2 
3 /*
4    nslcd-prot.h - helper macros for reading and writing in protocol streams
5 
6    Copyright (C) 2006 West Consulting
7    Copyright (C) 2006-2014 Arthur de Jong
8 
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 2.1 of the License, or (at your option) any later version.
13 
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Lesser General Public License for more details.
18 
19    You should have received a copy of the GNU Lesser General Public
20    License along with this library; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22    02110-1301 USA
23 */
24 
25 #ifndef COMMON__NSLCD_PROT_H
26 #define COMMON__NSLCD_PROT_H 1
27 
28 #include <arpa/inet.h>
29 #include <netinet/in.h>
30 
31 #include "tio.h"
32 
33 /* If you use these macros you should define the following macros to
34    handle error conditions (these marcos should clean up and return from the
35    function):
36      ERROR_OUT_WRITEERROR(fp)
37      ERROR_OUT_READERROR(fp)
38      ERROR_OUT_BUFERROR(fp)
39      ERROR_OUT_NOSUCCESS(fp) */
40 
41 
42 /* Debugging marcos that can be used to enable detailed protocol logging,
43    pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP
44    to dump the actual bytestream. */
45 
46 #ifdef DEBUG_PROT
47 /* define a debugging macro to output logging */
48 #include <string.h>
49 #include <errno.h>
50 #define DEBUG_PRINT(fmt, arg)                                               \
51   fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__,                \
52           __PRETTY_FUNCTION__, arg);
53 #else /* DEBUG_PROT */
54 /* define an empty debug macro to disable logging */
55 #define DEBUG_PRINT(fmt, arg)
56 #endif /* not DEBUG_PROT */
57 
58 #ifdef DEBUG_PROT_DUMP
59 /* define a debugging macro to output detailed logging */
60 #ifdef HAVE_STDINT_H
61 #include <stdint.h>
62 #endif /* HAVE_STDINT_H */
debug_dump(const void * ptr,size_t size)63 static void debug_dump(const void *ptr, size_t size)
64 {
65   int i;
66   for (i = 0; i < size; i++)
67     fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]);
68   fprintf(stderr, "\n");
69 }
70 #define DEBUG_DUMP(ptr, size)                                               \
71   fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__);    \
72   debug_dump(ptr, size);
73 #else /* DEBUG_PROT_DUMP */
74 /* define an empty debug macro to disable logging */
75 #define DEBUG_DUMP(ptr, size)
76 #endif /* not DEBUG_PROT_DUMP */
77 
78 
79 /* WRITE marcos, used for writing data, on write error they will
80    call the ERROR_OUT_WRITEERROR macro
81    these macros may require the availability of the following
82    variables:
83    int32_t tmpint32; - temporary variable
84    */
85 
86 #define WRITE(fp, ptr, size)                                                \
87   DEBUG_PRINT("WRITE       : var="__STRING(ptr)" size=%d", (int)size);      \
88   DEBUG_DUMP(ptr, size);                                                    \
89   if (tio_write(fp, ptr, (size_t)size))                                     \
90   {                                                                         \
91     char ebuf[128];                                                         \
92     int saved_errno = errno;                                                \
93     DEBUG_PRINT("WRITE       : var="__STRING(ptr)" error: %s",              \
94                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
95     ERROR_OUT_WRITEERROR(fp);                                               \
96   }
97 
98 #define WRITE_INT32(fp, i)                                                  \
99   DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i);        \
100   tmpint32 = htonl((int32_t)(i));                                           \
101   WRITE(fp, &tmpint32, sizeof(int32_t))
102 
103 #define WRITE_STRING(fp, str)                                               \
104   DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str));    \
105   if ((str) == NULL)                                                        \
106   {                                                                         \
107     WRITE_INT32(fp, 0);                                                     \
108   }                                                                         \
109   else                                                                      \
110   {                                                                         \
111     WRITE_INT32(fp, strlen(str));                                           \
112     tmpint32 = ntohl(tmpint32);                                             \
113     if (tmpint32 > 0)                                                       \
114     {                                                                       \
115       WRITE(fp, (str), tmpint32);                                           \
116     }                                                                       \
117   }
118 
119 #define WRITE_STRINGLIST(fp, arr)                                           \
120   if ((arr) == NULL)                                                        \
121   {                                                                         \
122     DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0);             \
123     WRITE_INT32(fp, 0);                                                     \
124   }                                                                         \
125   else                                                                      \
126   {                                                                         \
127     /* first determine length of array */                                   \
128     for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++)              \
129       /* noting */ ;                                                        \
130     /* write number of strings */                                           \
131     DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
132     WRITE_INT32(fp, tmp3int32);                                             \
133     /* write strings */                                                     \
134     for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++)                 \
135     {                                                                       \
136       WRITE_STRING(fp, (arr)[tmp2int32]);                                   \
137     }                                                                       \
138   }
139 
140 #define WRITE_STRINGLIST_EXCEPT(fp, arr, not)                               \
141   /* first determine length of array */                                     \
142   tmp3int32 = 0;                                                            \
143   for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++)                \
144     if (strcmp((arr)[tmp2int32], (not)) != 0)                               \
145       tmp3int32++;                                                          \
146   /* write number of strings (mius one because we intend to skip one) */    \
147   DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32);  \
148   WRITE_INT32(fp, tmp3int32);                                               \
149   /* write strings */                                                       \
150   for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++)                \
151   {                                                                         \
152     if (strcmp((arr)[tmp2int32], (not)) != 0)                               \
153     {                                                                       \
154       WRITE_STRING(fp, (arr)[tmp2int32]);                                   \
155     }                                                                       \
156   }
157 
158 /* READ macros, used for reading data, on read error they will
159    call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro
160    these macros may require the availability of the following
161    variables:
162    int32_t tmpint32; - temporary variable
163    */
164 
165 #define READ(fp, ptr, size)                                                 \
166   if (tio_read(fp, ptr, (size_t)size))                                      \
167   {                                                                         \
168     char ebuf[128];                                                         \
169     int saved_errno = errno;                                                \
170     DEBUG_PRINT("READ       : var="__STRING(ptr)" error: %s",               \
171                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
172     ERROR_OUT_READERROR(fp);                                                \
173   }                                                                         \
174   DEBUG_PRINT("READ       : var="__STRING(ptr)" size=%d", (int)(size));     \
175   DEBUG_DUMP(ptr, size);
176 
177 #define READ_INT32(fp, i)                                                   \
178   READ(fp, &tmpint32, sizeof(int32_t));                                     \
179   (i) = (int32_t)ntohl(tmpint32);                                           \
180   DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i));
181 
182 /* read a string in a fixed-size "normal" buffer */
183 #define READ_STRING(fp, buffer)                                             \
184   /* read the size of the string */                                         \
185   READ(fp, &tmpint32, sizeof(int32_t));                                     \
186   tmpint32 = ntohl(tmpint32);                                               \
187   DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32);   \
188   /* check if read would fit */                                             \
189   if (((size_t)tmpint32) >= sizeof(buffer))                                 \
190   {                                                                         \
191     /* will not fit */                                                      \
192     tmpint32 = (tmpint32 - sizeof(buffer)) + 1;                             \
193     DEBUG_PRINT("READ       : buffer %d bytes too small", tmpint32);        \
194     ERROR_OUT_BUFERROR(fp);                                                 \
195   }                                                                         \
196   /* read string from the stream */                                         \
197   if (tmpint32 > 0)                                                         \
198   {                                                                         \
199     READ(fp, buffer, (size_t)tmpint32);                                     \
200   }                                                                         \
201   /* null-terminate string in buffer */                                     \
202   buffer[tmpint32] = '\0';                                                  \
203   DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer);
204 
205 
206 /* READ BUF macros that read data into a pre-allocated buffer.
207    these macros may require the availability of the following
208    variables:
209    int32_t tmpint32; - temporary variable
210    char *buffer;     - pointer to a buffer for reading strings
211    size_t buflen;    - the size of the buffer
212    size_t bufptr;    - the current position in the buffer
213    */
214 
215 /* current position in the buffer */
216 #define BUF_CUR                                                             \
217   (buffer + bufptr)
218 
219 /* check that the buffer has sz bytes left in it */
220 #define BUF_CHECK(fp, sz)                                                   \
221   if ((bufptr + (size_t)(sz)) > buflen)                                     \
222   {                                                                         \
223     /* will not fit */                                                      \
224     tmpint32 = bufptr + (sz) - (buflen);                                    \
225     DEBUG_PRINT("READ       : buffer %d bytes too small", tmpint32);        \
226     ERROR_OUT_BUFERROR(fp);                                                 \
227   }
228 
229 /* move the buffer pointer */
230 #define BUF_SKIP(sz)                                                        \
231   bufptr += (size_t)(sz);
232 
233 /* move BUF_CUR forward so that it is aligned to the specified
234    type width */
235 #define BUF_ALIGN(fp, type)                                                 \
236   /* figure out number of bytes to skip forward */                         \
237   tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type)))    \
238               % sizeof(type);                                               \
239   /* check and skip */                                                      \
240   BUF_CHECK(fp, tmp2int32);                                                 \
241   BUF_SKIP(tmp2int32);
242 
243 /* allocate a piece of the buffer to store an array in */
244 #define BUF_ALLOC(fp, ptr, type, num)                                       \
245   /* align to the specified type width */                                   \
246   BUF_ALIGN(fp, type);                                                      \
247   /* check that we have enough room */                                      \
248   BUF_CHECK(fp, (size_t)(num) * sizeof(type));                              \
249   /* store the pointer */                                                   \
250   (ptr) = (type *)BUF_CUR;                                                  \
251   /* reserve the space */                                                   \
252   BUF_SKIP((size_t)(num) * sizeof(type));
253 
254 /* read a binary blob into the buffer */
255 #define READ_BUF(fp, ptr, sz)                                               \
256   /* check that there is enough room and read */                            \
257   BUF_CHECK(fp, sz);                                                        \
258   READ(fp, BUF_CUR, (size_t)sz);                                            \
259   /* store pointer and skip */                                              \
260   (ptr) = BUF_CUR;                                                          \
261   BUF_SKIP(sz);
262 
263 /* read string in the buffer (using buffer, buflen and bufptr)
264    and store the actual location of the string in field */
265 #define READ_BUF_STRING(fp, field)                                          \
266   /* read the size of the string */                                         \
267   READ(fp, &tmpint32, sizeof(int32_t));                                     \
268   tmpint32 = ntohl(tmpint32);                                               \
269   DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \
270   /* check if read would fit */                                             \
271   BUF_CHECK(fp, tmpint32 + 1);                                              \
272   /* read string from the stream */                                         \
273   if (tmpint32 > 0)                                                         \
274   {                                                                         \
275     READ(fp, BUF_CUR, (size_t)tmpint32);                                    \
276   }                                                                         \
277   /* null-terminate string in buffer */                                     \
278   BUF_CUR[tmpint32] = '\0';                                                 \
279   DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \
280   /* prepare result */                                                      \
281   (field) = BUF_CUR;                                                        \
282   BUF_SKIP(tmpint32 + 1);
283 
284 /* read an array from a stream and store it as a null-terminated
285    array list (size for the array is allocated) */
286 #define READ_BUF_STRINGLIST(fp, arr)                                        \
287   /* read the number of entries */                                          \
288   READ(fp, &tmp3int32, sizeof(int32_t));                                    \
289   tmp3int32 = ntohl(tmp3int32);                                             \
290   DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32);   \
291   /* allocate room for *char[num + 1] */                                      \
292   BUF_ALLOC(fp, arr, char *, tmp3int32 + 1);                                \
293   /* read all entries */                                                    \
294   for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++)                   \
295   {                                                                         \
296     READ_BUF_STRING(fp, (arr)[tmp2int32]);                                  \
297   }                                                                         \
298   /* set last entry to NULL */                                              \
299   (arr)[tmp2int32] = NULL;
300 
301 
302 /* SKIP macros for skipping over certain parts of the protocol stream. */
303 
304 /* skip a number of bytes forward */
305 #define SKIP(fp, sz)                                                        \
306   DEBUG_PRINT("READ       : skip %d bytes", (int)(sz));                     \
307   /* read (skip) the specified number of bytes */                           \
308   if (tio_skip(fp, sz))                                                     \
309   {                                                                         \
310     char ebuf[128];                                                         \
311     int saved_errno = errno;                                                \
312     DEBUG_PRINT("READ       : skip error: %s",                              \
313                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
314     ERROR_OUT_READERROR(fp);                                                \
315   }
316 
317 /* read a string from the stream but don't do anything with the result */
318 #define SKIP_STRING(fp)                                                     \
319   /* read the size of the string */                                         \
320   READ(fp, &tmpint32, sizeof(int32_t));                                     \
321   tmpint32 = ntohl(tmpint32);                                               \
322   DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32);                 \
323   /* read (skip) the specified number of bytes */                           \
324   SKIP(fp, tmpint32);
325 
326 /* skip a list of strings */
327 #define SKIP_STRINGLIST(fp)                                                 \
328   /* read the number of entries */                                          \
329   READ(fp, &tmp3int32, sizeof(int32_t));                                    \
330   tmp3int32 = ntohl(tmp3int32);                                             \
331   DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32);              \
332   /* read all entries */                                                    \
333   for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++)                   \
334   {                                                                         \
335     SKIP_STRING(fp);                                                        \
336   }
337 
338 
339 /* These are functions and macros for performing common operations in
340    the nslcd request/response protocol. */
341 
342 /* returns a socket to the server or NULL on error (see errno),
343    socket should be closed with tio_close() */
344 TFILE *nslcd_client_open(void)
345   MUST_USE;
346 
347 /* generic request code */
348 #define NSLCD_REQUEST(fp, action, writefn)                                  \
349   /* open a client socket */                                                \
350   if ((fp = nslcd_client_open()) == NULL)                                   \
351   {                                                                         \
352     ERROR_OUT_OPENERROR;                                                    \
353   }                                                                         \
354   /* write a request header with a request code */                          \
355   WRITE_INT32(fp, (int32_t)NSLCD_VERSION)                                   \
356   WRITE_INT32(fp, (int32_t)action)                                          \
357   /* write the request parameters (if any) */                               \
358   writefn;                                                                  \
359   /* flush the stream */                                                    \
360   if (tio_flush(fp) < 0)                                                    \
361   {                                                                         \
362     char ebuf[128];                                                         \
363     int saved_errno = errno;                                                \
364     DEBUG_PRINT("WRITE_FLUSH : error: %s",                                  \
365                 AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)));            \
366     ERROR_OUT_WRITEERROR(fp);                                               \
367   }                                                                         \
368   /* read and check response version number */                              \
369   READ(fp, &tmpint32, sizeof(int32_t));                                     \
370   tmpint32 = ntohl(tmpint32);                                               \
371   if (tmpint32 != (int32_t)NSLCD_VERSION)                                   \
372   {                                                                         \
373     ERROR_OUT_READERROR(fp);                                                \
374   }                                                                         \
375   /* read and check response request number */                              \
376   READ(fp, &tmpint32, sizeof(int32_t));                                     \
377   tmpint32 = ntohl(tmpint32);                                               \
378   if (tmpint32 != (int32_t)(action))                                        \
379   {                                                                         \
380     ERROR_OUT_READERROR(fp);                                                \
381   }
382 
383 /* Read the response code (the result code of the query) from
384    the stream. */
385 #define READ_RESPONSE_CODE(fp)                                              \
386   READ(fp, &tmpint32, sizeof(int32_t));                                     \
387   tmpint32 = ntohl(tmpint32);                                               \
388   if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN)                              \
389   {                                                                         \
390     ERROR_OUT_NOSUCCESS(fp);                                                \
391   }
392 
393 #endif /* not COMMON__NSLCD_PROT_H */
394