]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ypldap/yp.c
Merge bmake-20230622
[FreeBSD/FreeBSD.git] / usr.sbin / ypldap / yp.c
1 /*      $OpenBSD: yp.c,v 1.14 2015/02/11 01:26:00 pelikan Exp $ */
2 /*      $FreeBSD$ */
3 /*
4  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <sys/types.h>
20 #include <sys/param.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
23 #include <sys/select.h>
24 #include <sys/tree.h>
25
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28
29 #include <errno.h>
30 #include <event.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <limits.h>
38
39 #include <rpc/rpc.h>
40 #include <rpc/xdr.h>
41 #include <rpc/pmap_clnt.h>
42 #include <rpc/pmap_prot.h>
43 #include <rpc/pmap_rmt.h>
44 #include <rpcsvc/yp.h>
45 #include <rpcsvc/ypclnt.h>
46
47 #include "ypldap.h"
48
49 void    yp_dispatch(struct svc_req *, SVCXPRT *);
50 void    yp_disable_events(void);
51 void    yp_fd_event(int, short, void *);
52 int     yp_check(struct svc_req *);
53 int     yp_valid_domain(char *, struct ypresp_val *);
54 void    yp_make_val(struct ypresp_val *, char *, int);
55 void    yp_make_keyval(struct ypresp_key_val *, char *, char *);
56
57 static struct env       *env;
58
59 struct yp_event {
60         TAILQ_ENTRY(yp_event)    ye_entry;
61         struct event             ye_event;
62 };
63
64 struct yp_data {
65         SVCXPRT                 *yp_trans_udp;
66         SVCXPRT                 *yp_trans_tcp;
67         TAILQ_HEAD(, yp_event)   yd_events;
68 };
69
70 void
71 yp_disable_events(void)
72 {
73         struct yp_event *ye;
74
75         while ((ye = TAILQ_FIRST(&env->sc_yp->yd_events)) != NULL) {
76                 TAILQ_REMOVE(&env->sc_yp->yd_events, ye, ye_entry);
77                 event_del(&ye->ye_event);
78                 free(ye);
79         }
80 }
81
82 void
83 yp_enable_events(void)
84 {
85         int i;
86         struct yp_event *ye;
87
88         for (i = 0; i < getdtablesize(); i++) {
89                 if ((ye = calloc(1, sizeof(*ye))) == NULL)
90                         fatal(NULL);
91                 event_set(&ye->ye_event, i, EV_READ, yp_fd_event, NULL);
92                 event_add(&ye->ye_event, NULL);
93                 TAILQ_INSERT_TAIL(&env->sc_yp->yd_events, ye, ye_entry);
94         }
95 }
96
97 void
98 yp_fd_event(int fd, short event, void *p)
99 {
100         svc_getreq_common(fd);
101         yp_disable_events();
102         yp_enable_events();
103 }
104
105 void
106 yp_init(struct env *x_env)
107 {
108         struct yp_data  *yp;
109
110         if ((yp = calloc(1, sizeof(*yp))) == NULL)
111                 fatal(NULL);
112         TAILQ_INIT(&yp->yd_events);
113
114         env = x_env;
115         env->sc_yp = yp;
116         
117         (void)pmap_unset(YPPROG, YPVERS);
118
119         if ((yp->yp_trans_udp = svcudp_create(RPC_ANYSOCK)) == NULL)
120                 fatal("cannot create udp service");
121         if ((yp->yp_trans_tcp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL)
122                 fatal("cannot create tcp service");
123
124         if (!svc_register(yp->yp_trans_udp, YPPROG, YPVERS,
125             yp_dispatch, IPPROTO_UDP)) {
126                 fatal("unable to register (YPPROG, YPVERS, udp)");
127         }
128         if (!svc_register(yp->yp_trans_tcp, YPPROG, YPVERS,
129             yp_dispatch, IPPROTO_TCP)) {
130                 fatal("unable to register (YPPROG, YPVERS, tcp)");
131         }
132 }
133
134 /*
135  * lots of inspiration from ypserv by Mats O Jansson
136  */
137 void
138 yp_dispatch(struct svc_req *req, SVCXPRT *trans)
139 {
140         xdrproc_t                xdr_argument;
141         xdrproc_t                xdr_result;
142         char                    *result;
143         char                    *(*cb)(char *, struct svc_req *);
144         union {
145                 domainname       ypproc_domain_2_arg;
146                 domainname       ypproc_domain_nonack_2_arg;
147                 ypreq_key        ypproc_match_2_arg;
148                 ypreq_nokey      ypproc_first_2_arg;
149                 ypreq_key        ypproc_next_2_arg;
150                 ypreq_xfr        ypproc_xfr_2_arg;
151                 ypreq_nokey      ypproc_all_2_arg;
152                 ypreq_nokey      ypproc_master_2_arg;
153                 ypreq_nokey      ypproc_order_2_arg;
154                 domainname       ypproc_maplist_2_arg;
155         } argument;
156
157         xdr_argument = (xdrproc_t) xdr_void;
158         xdr_result = (xdrproc_t) xdr_void;
159         cb = NULL;
160         switch (req->rq_proc) {
161         case YPPROC_NULL:
162                 xdr_argument = (xdrproc_t) xdr_void;
163                 xdr_result = (xdrproc_t) xdr_void;
164                 if (yp_check(req) == -1)
165                         return;
166                 result = NULL;
167                 if (!svc_sendreply(trans, (xdrproc_t) xdr_void,
168                     (void *)&result))
169                         svcerr_systemerr(trans);
170                 return;
171         case YPPROC_DOMAIN:
172                 xdr_argument = (xdrproc_t) xdr_domainname;
173                 xdr_result = (xdrproc_t) xdr_bool;
174                 if (yp_check(req) == -1)
175                         return;
176                 cb = (void *)ypproc_domain_2_svc;
177                 break;
178         case YPPROC_DOMAIN_NONACK:
179                 xdr_argument = (xdrproc_t) xdr_domainname;
180                 xdr_result = (xdrproc_t) xdr_bool;
181                 if (yp_check(req) == -1)
182                         return;
183                 cb = (void *)ypproc_domain_nonack_2_svc;
184                 break;
185         case YPPROC_MATCH:
186                 xdr_argument = (xdrproc_t) xdr_ypreq_key;
187                 xdr_result = (xdrproc_t) xdr_ypresp_val;
188                 if (yp_check(req) == -1)
189                         return;
190                 cb = (void *)ypproc_match_2_svc;
191                 break;
192         case YPPROC_FIRST:
193                 xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
194                 xdr_result = (xdrproc_t) xdr_ypresp_key_val;
195                 if (yp_check(req) == -1)
196                         return;
197                 cb = (void *)ypproc_first_2_svc;
198                 break;
199         case YPPROC_NEXT:
200                 xdr_argument = (xdrproc_t) xdr_ypreq_key;
201                 xdr_result = (xdrproc_t) xdr_ypresp_key_val;
202                 if (yp_check(req) == -1)
203                         return;
204                 cb = (void *)ypproc_next_2_svc;
205                 break;
206         case YPPROC_XFR:
207                 if (yp_check(req) == -1)
208                         return;
209                 svcerr_noproc(trans);
210                 return;
211         case YPPROC_CLEAR:
212                 log_debug("ypproc_clear");
213                 if (yp_check(req) == -1)
214                         return;
215                 svcerr_noproc(trans);
216                 return;
217         case YPPROC_ALL:
218                 log_debug("ypproc_all");
219                 xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
220                 xdr_result = (xdrproc_t) xdr_ypresp_all;
221                 if (yp_check(req) == -1)
222                         return;
223                 cb = (void *)ypproc_all_2_svc;
224                 break;
225         case YPPROC_MASTER:
226                 log_debug("ypproc_master");
227                 xdr_argument = (xdrproc_t) xdr_ypreq_nokey;
228                 xdr_result = (xdrproc_t) xdr_ypresp_master;
229                 if (yp_check(req) == -1)
230                         return;
231                 cb = (void *)ypproc_master_2_svc;
232                 break;
233         case YPPROC_ORDER:
234                 log_debug("ypproc_order");
235                 if (yp_check(req) == -1)
236                         return;
237                 svcerr_noproc(trans);
238                 return;
239         case YPPROC_MAPLIST:
240                 log_debug("ypproc_maplist");
241                 xdr_argument = (xdrproc_t) xdr_domainname;
242                 xdr_result = (xdrproc_t) xdr_ypresp_maplist;
243                 if (yp_check(req) == -1)
244                         return;
245                 cb = (void *)ypproc_maplist_2_svc;
246                 break;
247         default:
248                 svcerr_noproc(trans);
249                 return;
250         }
251         (void)memset(&argument, 0, sizeof(argument));
252
253         if (!svc_getargs(trans, xdr_argument, (caddr_t)&argument)) {
254                 svcerr_decode(trans);
255                 return;
256         }
257         result = (*cb)((char *)&argument, req);
258         if (result != NULL && !svc_sendreply(trans, xdr_result, result))
259                 svcerr_systemerr(trans);
260         if (!svc_freeargs(trans, xdr_argument, (caddr_t)&argument)) {
261                 /*
262                  * ypserv does it too.
263                  */
264                 fatal("unable to free arguments");
265         }
266 }
267
268 int
269 yp_check(struct svc_req *req)
270 {
271         /*
272          * We might want to know who we allow here.
273          */
274         return (0);
275 }
276
277 int
278 yp_valid_domain(char *domain, struct ypresp_val *res)
279 {
280         if (domain == NULL) {
281                 log_debug("NULL domain !");
282                 return (-1);
283         }
284         if (strcmp(domain, env->sc_domainname) != 0) {
285                 res->stat = YP_NODOM;
286                 return (-1);
287         }
288         return (0);
289 }
290
291 bool_t *
292 ypproc_domain_2_svc(domainname *arg, struct svc_req *req)
293 {
294         static bool_t   res;
295         
296         res = (bool_t)1;
297         if (strcmp(*arg, env->sc_domainname) != 0)
298                 res = (bool_t)0;
299         return (&res);
300 }
301
302 bool_t *
303 ypproc_domain_nonack_2_svc(domainname *arg, struct svc_req *req)
304 {
305         static bool_t   res;
306         
307         if (strcmp(*arg, env->sc_domainname) != 0)
308                 return NULL;
309         res = (bool_t)1;
310         return (&res);
311 }
312
313 ypresp_val *
314 ypproc_match_2_svc(ypreq_key *arg, struct svc_req *req)
315 {
316         struct userent           ukey;
317         struct userent          *ue;
318         struct groupent          gkey;
319         struct groupent         *ge;
320         static struct ypresp_val res;
321         const char              *estr;
322         char                    *bp, *cp;
323         char                     *key;
324
325         log_debug("matching '%.*s' in map %s", arg->key.keydat_len,
326            arg->key.keydat_val, arg->map);
327
328         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
329                 return (&res);
330
331         if (env->sc_user_names == NULL) {
332                 /*
333                  * tree not ready.
334                  */
335                 return (NULL);
336         }
337
338         if (arg->key.keydat_len > YPMAXRECORD) {
339                 log_debug("argument too long");
340                 return (NULL);
341         }
342         key = calloc(arg->key.keydat_len + 1, 1);
343         if (key == NULL)
344                 return (NULL);
345         (void)strncpy(key, arg->key.keydat_val, arg->key.keydat_len);
346
347         if (strcmp(arg->map, "passwd.byname") == 0 ||
348             strcmp(arg->map, "master.passwd.byname") == 0) {
349                 ukey.ue_line = key;
350                 if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
351                     &ukey)) == NULL) {
352                         res.stat = YP_NOKEY;
353                         goto out;
354                 }
355
356                 yp_make_val(&res, ue->ue_line, 1);
357                 goto out;
358         } else if (strcmp(arg->map, "passwd.byuid") == 0 ||
359                    strcmp(arg->map, "master.passwd.byuid") == 0) {
360                 ukey.ue_uid = strtonum(key, 0, UID_MAX, &estr); 
361                 if (estr) {
362                         res.stat = YP_BADARGS;
363                         goto out;
364                 }
365
366                 if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
367                     &ukey)) == NULL) {
368                         res.stat = YP_NOKEY;
369                         goto out;
370                 }
371
372                 yp_make_val(&res, ue->ue_line, 1);
373                 return (&res);
374         } else if (strcmp(arg->map, "group.bygid") == 0) {
375                 gkey.ge_gid = strtonum(key, 0, GID_MAX, &estr); 
376                 if (estr) {
377                         res.stat = YP_BADARGS;
378                         goto out;
379                 }
380                 if ((ge = RB_FIND(group_gid_tree, &env->sc_group_gids,
381                     &gkey)) == NULL) {
382                         res.stat = YP_NOKEY;
383                         goto out;
384                 }
385
386                 yp_make_val(&res, ge->ge_line, 1);
387                 return (&res);
388         } else if (strcmp(arg->map, "group.byname") == 0) {
389                 gkey.ge_line = key;
390                 if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
391                     &gkey)) == NULL) {
392                         res.stat = YP_NOKEY;
393                         goto out;
394                 }
395
396                 yp_make_val(&res, ge->ge_line, 1);
397                 return (&res);
398         } else if (strcmp(arg->map, "netid.byname") == 0) {
399                 bp = cp = key;
400
401                 if (strncmp(bp, "unix.", strlen("unix.")) != 0) {
402                         res.stat = YP_BADARGS;
403                         goto out;
404                 }
405
406                 bp += strlen("unix.");
407
408                 if (*bp == '\0') {
409                         res.stat = YP_BADARGS;
410                         goto out;
411                 }
412
413                 if (!(cp = strsep(&bp, "@"))) {
414                         res.stat = YP_BADARGS;
415                         goto out;
416                 }
417
418                 if (strcmp(bp, arg->domain) != 0) {
419                         res.stat = YP_BADARGS;
420                         goto out;
421                 }
422
423                 ukey.ue_uid = strtonum(cp, 0, UID_MAX, &estr); 
424                 if (estr) {
425                         res.stat = YP_BADARGS;
426                         goto out;
427                 }
428
429                 if ((ue = RB_FIND(user_uid_tree, &env->sc_user_uids,
430                     &ukey)) == NULL) {
431                         res.stat = YP_NOKEY;
432                         goto out;
433                 }
434
435                 yp_make_val(&res, ue->ue_netid_line, 0);
436                 goto out;
437         
438         } else {
439                 log_debug("unknown map %s", arg->map);
440                 res.stat = YP_NOMAP;
441                 goto out;
442         }
443 out:
444         free(key);
445         return (&res);
446 }
447
448 ypresp_key_val *
449 ypproc_first_2_svc(ypreq_nokey *arg, struct svc_req *req)
450 {
451         static struct ypresp_key_val    res;
452
453         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
454                 return (&res);
455
456         if (strcmp(arg->map, "passwd.byname") == 0 ||
457             strcmp(arg->map, "master.passwd.byname") == 0) {
458                 if (env->sc_user_lines == NULL)
459                         return (NULL);
460
461                 yp_make_keyval(&res, env->sc_user_lines, env->sc_user_lines);
462         } else if (strcmp(arg->map, "group.byname") == 0) {
463                 if (env->sc_group_lines == NULL)
464                         return (NULL);
465
466                 yp_make_keyval(&res, env->sc_group_lines, env->sc_group_lines);
467         } else {
468                 log_debug("unknown map %s", arg->map);
469                 res.stat = YP_NOMAP;
470         }
471
472         return (&res);
473 }
474
475 ypresp_key_val *
476 ypproc_next_2_svc(ypreq_key *arg, struct svc_req *req)
477 {
478         struct userent                   ukey;
479         struct userent                  *ue;
480         struct groupent                  gkey;
481         struct groupent                 *ge;
482         char                            *line;
483         static struct ypresp_key_val     res;
484         char                             *key;
485
486         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
487                 return (&res);
488
489         key = NULL;
490         if (strcmp(arg->map, "passwd.byname") == 0 ||
491             strcmp(arg->map, "master.passwd.byname") == 0) {
492                 key = calloc(arg->key.keydat_len + 1, 1);
493                 if (key == NULL) {
494                         res.stat = YP_YPERR;
495                         return (&res);
496                 }
497                 (void)strncpy(key, arg->key.keydat_val,
498                     arg->key.keydat_len);
499                 ukey.ue_line = key;
500                 if ((ue = RB_FIND(user_name_tree, env->sc_user_names,
501                     &ukey)) == NULL) {
502                         /*
503                          * canacar's trick:
504                          * the user might have been deleted in between calls
505                          * to next since the tree may be modified by a reload.
506                          * next should still return the next user in
507                          * lexicographical order, hence insert the search key
508                          * and look up the next field, then remove it again.
509                          */
510                         RB_INSERT(user_name_tree, env->sc_user_names, &ukey);
511                         if ((ue = RB_NEXT(user_name_tree, &env->sc_user_names,
512                             &ukey)) == NULL) {
513                                 RB_REMOVE(user_name_tree, env->sc_user_names,
514                                     &ukey);
515                                 res.stat = YP_NOKEY;
516                                 free(key);
517                                 return (&res);
518                         }
519                         RB_REMOVE(user_name_tree, env->sc_user_names, &ukey);
520                 }
521                 line = ue->ue_line + (strlen(ue->ue_line) + 1);
522                 line = line + (strlen(line) + 1);
523                 yp_make_keyval(&res, line, line);
524                 free(key);
525                 return (&res);
526
527
528         } else if (strcmp(arg->map, "group.byname") == 0) {
529                 key = calloc(arg->key.keydat_len + 1, 1);
530                 if (key == NULL) {
531                         res.stat = YP_YPERR;
532                         return (&res);
533                 }
534                 (void)strncpy(key, arg->key.keydat_val,
535                     arg->key.keydat_len);
536                 
537                 gkey.ge_line = key;
538                 if ((ge = RB_FIND(group_name_tree, env->sc_group_names,
539                     &gkey)) == NULL) {
540                         /*
541                          * canacar's trick reloaded.
542                          */
543                         RB_INSERT(group_name_tree, env->sc_group_names, &gkey);
544                         if ((ge = RB_NEXT(group_name_tree, &env->sc_group_names,
545                             &gkey)) == NULL) {
546                                 RB_REMOVE(group_name_tree, env->sc_group_names,
547                                     &gkey);
548                                 res.stat = YP_NOKEY;
549                                 free(key);
550                                 return (&res);
551                         }
552                         RB_REMOVE(group_name_tree, env->sc_group_names, &gkey);
553                 }
554
555                 line = ge->ge_line + (strlen(ge->ge_line) + 1);
556                 line = line + (strlen(line) + 1);
557                 yp_make_keyval(&res, line, line);
558                 free(key);
559                 return (&res);
560         } else {
561                 log_debug("unknown map %s", arg->map);
562                 res.stat = YP_NOMAP;
563                 return (&res);
564         }
565 }
566
567 ypresp_all *
568 ypproc_all_2_svc(ypreq_nokey *arg, struct svc_req *req)
569 {
570         static struct ypresp_all        res;
571
572         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
573                 return (&res);
574
575         svcerr_auth(req->rq_xprt, AUTH_FAILED);
576         return (NULL);
577 }
578
579 ypresp_master *
580 ypproc_master_2_svc(ypreq_nokey *arg, struct svc_req *req)
581 {
582         static struct ypresp_master      res;
583         static char master[YPMAXPEER + 1];
584
585         memset(&res, 0, sizeof(res));
586         if (yp_valid_domain(arg->domain, (struct ypresp_val *)&res) == -1)
587                 return (&res);
588         
589         if (gethostname(master, sizeof(master)) == 0) {
590                 res.peer = (peername)master;
591                 res.stat = YP_TRUE;
592         } else
593                 res.stat = YP_NOKEY;
594
595         return (&res);
596 }
597
598 ypresp_maplist *
599 ypproc_maplist_2_svc(domainname *arg, struct svc_req *req)
600 {
601         size_t                   i;
602         static struct {
603                 char            *name;
604                 int              cond;
605         }                        mapnames[] = {
606                 { "passwd.byname",              YPMAP_PASSWD_BYNAME },
607                 { "passwd.byuid",               YPMAP_PASSWD_BYUID },
608                 { "master.passwd.byname",       YPMAP_MASTER_PASSWD_BYNAME },
609                 { "master.passwd.byuid",        YPMAP_MASTER_PASSWD_BYUID },
610                 { "group.byname",               YPMAP_GROUP_BYNAME },
611                 { "group.bygid",                YPMAP_GROUP_BYGID },
612                 { "netid.byname",               YPMAP_NETID_BYNAME },
613         };
614         static ypresp_maplist    res;
615         static struct ypmaplist  maps[nitems(mapnames)];
616         
617         if (yp_valid_domain(*arg, (struct ypresp_val *)&res) == -1)
618                 return (&res);
619
620         res.stat = YP_TRUE;
621         res.maps = NULL;
622         for (i = 0; i < nitems(mapnames); i++) {
623                 if (!(env->sc_flags & mapnames[i].cond))
624                         continue;
625                 maps[i].map = mapnames[i].name;
626                 maps[i].next = res.maps;
627                 res.maps = &maps[i];
628         }
629
630         return (&res);
631 }
632
633 void
634 yp_make_val(struct ypresp_val *res, char *line, int replacecolon)
635 {
636         static char              buf[LINE_WIDTH];
637
638         memset(buf, 0, sizeof(buf));
639
640         if (replacecolon)
641                 line[strlen(line)] = ':';
642         (void)strlcpy(buf, line, sizeof(buf));
643         if (replacecolon)
644                 line[strcspn(line, ":")] = '\0';
645         log_debug("sending out %s", buf);
646
647         res->stat = YP_TRUE;
648         res->val.valdat_len = strlen(buf);
649         res->val.valdat_val = buf;
650 }
651
652 void
653 yp_make_keyval(struct ypresp_key_val *res, char *key, char *line)
654 {
655         static char     keybuf[YPMAXRECORD+1];
656         static char     buf[LINE_WIDTH];
657
658         memset(keybuf, 0, sizeof(keybuf));
659         memset(buf, 0, sizeof(buf));
660         
661         (void)strlcpy(keybuf, key, sizeof(keybuf));
662         res->key.keydat_len = strlen(keybuf);
663         res->key.keydat_val = keybuf;
664
665         if (*line == '\0') {
666                 res->stat = YP_NOMORE;
667                 return;
668         }
669         res->stat = YP_TRUE;
670         line[strlen(line)] = ':';
671         (void)strlcpy(buf, line, sizeof(buf));
672         line[strcspn(line, ":")] = '\0';
673         log_debug("sending out %s => %s", keybuf, buf);
674
675         res->val.valdat_len = strlen(buf);
676         res->val.valdat_val = buf;
677 }