GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpd/snmpe.c Lines: 0 324 0.0 %
Date: 2017-11-07 Branches: 0 184 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: snmpe.c,v 1.50 2017/08/12 16:31:09 florian Exp $	*/
2
3
/*
4
 * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@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/queue.h>
20
#include <sys/types.h>
21
#include <sys/stat.h>
22
#include <sys/socket.h>
23
#include <sys/un.h>
24
#include <sys/tree.h>
25
26
#include <net/if.h>
27
#include <netinet/in.h>
28
#include <arpa/inet.h>
29
30
#include <stdlib.h>
31
#include <stdio.h>
32
#include <errno.h>
33
#include <event.h>
34
#include <fcntl.h>
35
#include <string.h>
36
#include <unistd.h>
37
#include <pwd.h>
38
39
#include "snmpd.h"
40
#include "mib.h"
41
42
void	 snmpe_init(struct privsep *, struct privsep_proc *, void *);
43
int	 snmpe_parse(struct snmp_message *);
44
int	 snmpe_parsevarbinds(struct snmp_message *);
45
void	 snmpe_response(struct snmp_message *);
46
unsigned long
47
	 snmpe_application(struct ber_element *);
48
void	 snmpe_sig_handler(int sig, short, void *);
49
int	 snmpe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
50
int	 snmpe_bind(struct address *);
51
void	 snmpe_recvmsg(int fd, short, void *);
52
int	 snmpe_encode(struct snmp_message *);
53
void	 snmp_msgfree(struct snmp_message *);
54
55
struct imsgev	*iev_parent;
56
57
static struct privsep_proc procs[] = {
58
	{ "parent",	PROC_PARENT,	snmpe_dispatch_parent }
59
};
60
61
void
62
snmpe(struct privsep *ps, struct privsep_proc *p)
63
{
64
	struct snmpd		*env = ps->ps_env;
65
	struct address		*h;
66
	struct listen_sock	*so;
67
#ifdef DEBUG
68
	char		 buf[BUFSIZ];
69
	struct oid	*oid;
70
#endif
71
72
#ifdef DEBUG
73
	for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) {
74
		smi_oid2string(&oid->o_id, buf, sizeof(buf), 0);
75
		log_debug("oid %s", buf);
76
	}
77
#endif
78
79
	TAILQ_FOREACH(h, &env->sc_addresses, entry) {
80
		if ((so = calloc(1, sizeof(*so))) == NULL)
81
			fatal("snmpe: %s", __func__);
82
		if ((so->s_fd = snmpe_bind(h)) == -1)
83
			fatal("snmpe: failed to bind SNMP UDP socket");
84
		TAILQ_INSERT_TAIL(&env->sc_sockets, so, entry);
85
	}
86
87
	proc_run(ps, p, procs, nitems(procs), snmpe_init, NULL);
88
}
89
90
/* ARGSUSED */
91
void
92
snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
93
{
94
	struct snmpd		*env = ps->ps_env;
95
	struct listen_sock	*so;
96
97
	kr_init();
98
	trap_init();
99
	timer_init();
100
	usm_generate_keys();
101
102
	/* listen for incoming SNMP UDP messages */
103
	TAILQ_FOREACH(so, &env->sc_sockets, entry) {
104
		event_set(&so->s_ev, so->s_fd, EV_READ|EV_PERSIST,
105
		    snmpe_recvmsg, env);
106
		event_add(&so->s_ev, NULL);
107
	}
108
109
#if 0
110
	/*
111
	 * XXX Refactoring required to move illegal ioctls and sysctls.
112
	 * XXX See mps_* and if_mib in mib.c, etc.
113
	 */
114
BROKEN	if (pledge("stdio inet route recvfd vminfo flock rpath cpath wpath", NULL) == -1)
115
		fatal("pledge");
116
#endif
117
}
118
119
void
120
snmpe_shutdown(void)
121
{
122
	kr_shutdown();
123
}
124
125
int
126
snmpe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
127
{
128
	switch (imsg->hdr.type) {
129
	default:
130
		break;
131
	}
132
133
	return (-1);
134
}
135
136
int
137
snmpe_bind(struct address *addr)
138
{
139
	char	 buf[512];
140
	int	 val, s;
141
142
	if ((s = snmpd_socket_af(&addr->ss, htons(addr->port))) == -1)
143
		return (-1);
144
145
	/*
146
	 * Socket options
147
	 */
148
	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
149
		goto bad;
150
151
	switch (addr->ss.ss_family) {
152
	case AF_INET:
153
		val = 1;
154
		if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
155
		    &val, sizeof(int)) == -1) {
156
			log_warn("%s: failed to set IPv4 packet info",
157
			    __func__);
158
			goto bad;
159
		}
160
		break;
161
	case AF_INET6:
162
		val = 1;
163
		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
164
		    &val, sizeof(int)) == -1) {
165
			log_warn("%s: failed to set IPv6 packet info",
166
			    __func__);
167
			goto bad;
168
		}
169
	}
170
171
	if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
172
		goto bad;
173
174
	if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
175
		goto bad;
176
177
	log_info("snmpe: listening on %s:%d", buf, addr->port);
178
179
	return (s);
180
181
 bad:
182
	close(s);
183
	return (-1);
184
}
185
186
int
187
snmpe_parse(struct snmp_message *msg)
188
{
189
	struct snmpd		*env = snmpd_env;
190
	struct snmp_stats	*stats = &env->sc_stats;
191
	struct ber_element	*a;
192
	long long		 ver, req;
193
	long long		 errval, erridx;
194
	unsigned long		 type;
195
	u_int			 class;
196
	char			*comn;
197
	char			*flagstr, *ctxname;
198
	size_t			 len;
199
	struct sockaddr_storage *ss = &msg->sm_ss;
200
	struct ber_element	*root = msg->sm_req;
201
202
	msg->sm_errstr = "invalid message";
203
204
	if (ber_scanf_elements(root, "{ie", &ver, &a) != 0)
205
		goto parsefail;
206
207
	/* SNMP version and community */
208
	msg->sm_version = ver;
209
	switch (msg->sm_version) {
210
	case SNMP_V1:
211
	case SNMP_V2:
212
		if (env->sc_min_seclevel != 0)
213
			goto badversion;
214
		if (ber_scanf_elements(a, "se", &comn, &msg->sm_pdu) != 0)
215
			goto parsefail;
216
		if (strlcpy(msg->sm_community, comn,
217
		    sizeof(msg->sm_community)) >= sizeof(msg->sm_community)) {
218
			stats->snmp_inbadcommunitynames++;
219
			msg->sm_errstr = "community name too long";
220
			goto fail;
221
		}
222
		break;
223
	case SNMP_V3:
224
		if (ber_scanf_elements(a, "{iisi}e",
225
		    &msg->sm_msgid, &msg->sm_max_msg_size, &flagstr,
226
		    &msg->sm_secmodel, &a) != 0)
227
			goto parsefail;
228
229
		msg->sm_flags = *flagstr;
230
		if (MSG_SECLEVEL(msg) < env->sc_min_seclevel ||
231
		    msg->sm_secmodel != SNMP_SEC_USM) {
232
			/* XXX currently only USM supported */
233
			msg->sm_errstr = "unsupported security model";
234
			stats->snmp_usmbadseclevel++;
235
			msg->sm_usmerr = OIDVAL_usmErrSecLevel;
236
			goto parsefail;
237
		}
238
239
		if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL)
240
			goto parsefail;
241
242
		if (ber_scanf_elements(a, "{xxe",
243
		    &msg->sm_ctxengineid, &msg->sm_ctxengineid_len,
244
		    &ctxname, &len, &msg->sm_pdu) != 0)
245
			goto parsefail;
246
		if (len > SNMPD_MAXCONTEXNAMELEN)
247
			goto parsefail;
248
		memcpy(msg->sm_ctxname, ctxname, len);
249
		msg->sm_ctxname[len] = '\0';
250
		break;
251
	default:
252
	badversion:
253
		stats->snmp_inbadversions++;
254
		msg->sm_errstr = "bad snmp version";
255
		goto fail;
256
	}
257
258
	if (ber_scanf_elements(msg->sm_pdu, "t{e", &class, &type, &a) != 0)
259
		goto parsefail;
260
261
	/* SNMP PDU context */
262
	if (class != BER_CLASS_CONTEXT)
263
		goto parsefail;
264
265
	switch (type) {
266
	case SNMP_C_GETBULKREQ:
267
		if (msg->sm_version == SNMP_V1) {
268
			stats->snmp_inbadversions++;
269
			msg->sm_errstr =
270
			    "invalid request for protocol version 1";
271
			goto fail;
272
		}
273
		/* FALLTHROUGH */
274
275
	case SNMP_C_GETREQ:
276
		stats->snmp_ingetrequests++;
277
		/* FALLTHROUGH */
278
279
	case SNMP_C_GETNEXTREQ:
280
		if (type == SNMP_C_GETNEXTREQ)
281
			stats->snmp_ingetnexts++;
282
		if (msg->sm_version != SNMP_V3 &&
283
		    strcmp(env->sc_rdcommunity, msg->sm_community) != 0 &&
284
		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
285
			stats->snmp_inbadcommunitynames++;
286
			msg->sm_errstr = "wrong read community";
287
			goto fail;
288
		}
289
		msg->sm_context = type;
290
		break;
291
292
	case SNMP_C_SETREQ:
293
		stats->snmp_insetrequests++;
294
		if (msg->sm_version != SNMP_V3 &&
295
		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
296
			if (strcmp(env->sc_rdcommunity, msg->sm_community) != 0)
297
				stats->snmp_inbadcommunitynames++;
298
			else
299
				stats->snmp_inbadcommunityuses++;
300
			msg->sm_errstr = "wrong write community";
301
			goto fail;
302
		}
303
		msg->sm_context = type;
304
		break;
305
306
	case SNMP_C_GETRESP:
307
		stats->snmp_ingetresponses++;
308
		msg->sm_errstr = "response without request";
309
		goto parsefail;
310
311
	case SNMP_C_TRAP:
312
	case SNMP_C_TRAPV2:
313
		if (msg->sm_version != SNMP_V3 &&
314
		    strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
315
			stats->snmp_inbadcommunitynames++;
316
			msg->sm_errstr = "wrong trap community";
317
			goto fail;
318
		}
319
		stats->snmp_intraps++;
320
		msg->sm_errstr = "received trap";
321
		goto fail;
322
323
	default:
324
		msg->sm_errstr = "invalid context";
325
		goto parsefail;
326
	}
327
328
	/* SNMP PDU */
329
	if (ber_scanf_elements(a, "iiie{et",
330
	    &req, &errval, &erridx, &msg->sm_pduend,
331
	    &msg->sm_varbind, &class, &type) != 0) {
332
		stats->snmp_silentdrops++;
333
		msg->sm_errstr = "invalid PDU";
334
		goto fail;
335
	}
336
	if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) {
337
		stats->snmp_silentdrops++;
338
		msg->sm_errstr = "invalid varbind";
339
		goto fail;
340
	}
341
342
	msg->sm_request = req;
343
	msg->sm_error = errval;
344
	msg->sm_errorindex = erridx;
345
346
	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
347
	if (msg->sm_version == SNMP_V3)
348
		log_debug("%s: %s: SNMPv3 context %d, flags %#x, "
349
		    "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', "
350
		    "request %lld", __func__, msg->sm_host, msg->sm_context,
351
		    msg->sm_flags, msg->sm_secmodel, msg->sm_username,
352
		    tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len),
353
		    msg->sm_ctxname, msg->sm_request);
354
	else
355
		log_debug("%s: %s: SNMPv%d '%s' context %d request %lld",
356
		    __func__, msg->sm_host, msg->sm_version + 1,
357
		    msg->sm_community, msg->sm_context, msg->sm_request);
358
359
	return (0);
360
361
 parsefail:
362
	stats->snmp_inasnparseerrs++;
363
 fail:
364
	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
365
	log_debug("%s: %s: %s", __func__, msg->sm_host, msg->sm_errstr);
366
	return (-1);
367
}
368
369
int
370
snmpe_parsevarbinds(struct snmp_message *msg)
371
{
372
	struct snmp_stats	*stats = &snmpd_env->sc_stats;
373
	char			 buf[BUFSIZ];
374
	struct ber_oid		 o;
375
	int			 ret = 0;
376
377
	msg->sm_errstr = "invalid varbind element";
378
379
	if (msg->sm_i == 0) {
380
		msg->sm_i = 1;
381
		msg->sm_a = msg->sm_varbind;
382
		msg->sm_last = NULL;
383
	}
384
385
	 for (; msg->sm_a != NULL && msg->sm_i < SNMPD_MAXVARBIND;
386
	    msg->sm_a = msg->sm_next, msg->sm_i++) {
387
		msg->sm_next = msg->sm_a->be_next;
388
389
		if (msg->sm_a->be_class != BER_CLASS_UNIVERSAL ||
390
		    msg->sm_a->be_type != BER_TYPE_SEQUENCE)
391
			continue;
392
		if ((msg->sm_b = msg->sm_a->be_sub) == NULL)
393
			continue;
394
395
		for (msg->sm_state = 0; msg->sm_state < 2 && msg->sm_b != NULL;
396
		    msg->sm_b = msg->sm_b->be_next) {
397
			switch (msg->sm_state++) {
398
			case 0:
399
				if (ber_get_oid(msg->sm_b, &o) != 0)
400
					goto varfail;
401
				if (o.bo_n < BER_MIN_OID_LEN ||
402
				    o.bo_n > BER_MAX_OID_LEN)
403
					goto varfail;
404
				if (msg->sm_context == SNMP_C_SETREQ)
405
					stats->snmp_intotalsetvars++;
406
				else
407
					stats->snmp_intotalreqvars++;
408
				log_debug("%s: %s: oid %s",
409
				    __func__, msg->sm_host,
410
				    smi_oid2string(&o, buf, sizeof(buf), 0));
411
				break;
412
			case 1:
413
				msg->sm_c = NULL;
414
				msg->sm_end = NULL;
415
416
				switch (msg->sm_context) {
417
418
				case SNMP_C_GETNEXTREQ:
419
					msg->sm_c = ber_add_sequence(NULL);
420
					ret = mps_getnextreq(msg, msg->sm_c,
421
					    &o);
422
					if (ret == 0 || ret == 1)
423
						break;
424
					ber_free_elements(msg->sm_c);
425
					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
426
					goto varfail;
427
428
				case SNMP_C_GETREQ:
429
					msg->sm_c = ber_add_sequence(NULL);
430
					ret = mps_getreq(msg, msg->sm_c, &o,
431
					    msg->sm_version);
432
					if (ret == 0 || ret == 1)
433
						break;
434
					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
435
					ber_free_elements(msg->sm_c);
436
					goto varfail;
437
438
				case SNMP_C_SETREQ:
439
					if (snmpd_env->sc_readonly == 0) {
440
						ret = mps_setreq(msg,
441
						    msg->sm_b, &o);
442
						if (ret == 0)
443
							break;
444
					}
445
					msg->sm_error = SNMP_ERROR_READONLY;
446
					goto varfail;
447
448
				case SNMP_C_GETBULKREQ:
449
					ret = mps_getbulkreq(msg, &msg->sm_c,
450
					    &msg->sm_end, &o,
451
					    (msg->sm_i <= msg->sm_nonrepeaters)
452
					    ? 1 : msg->sm_maxrepetitions);
453
					if (ret == 0 || ret == 1)
454
						break;
455
					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
456
					goto varfail;
457
458
				default:
459
					goto varfail;
460
				}
461
				if (msg->sm_c == NULL)
462
					break;
463
				if (msg->sm_end == NULL)
464
					msg->sm_end = msg->sm_c;
465
				if (msg->sm_last == NULL)
466
					msg->sm_varbindresp = msg->sm_c;
467
				else
468
					ber_link_elements(msg->sm_last, msg->sm_c);
469
				msg->sm_last = msg->sm_end;
470
				break;
471
			}
472
		}
473
		if (msg->sm_state < 2)  {
474
			log_debug("%s: state %d", __func__, msg->sm_state);
475
			goto varfail;
476
		}
477
	}
478
479
	msg->sm_errstr = "none";
480
	msg->sm_error = 0;
481
	msg->sm_errorindex = 0;
482
483
	return (ret);
484
 varfail:
485
	log_debug("%s: %s: %s, error index %d", __func__,
486
	    msg->sm_host, msg->sm_errstr, msg->sm_i);
487
	if (msg->sm_error == 0)
488
		msg->sm_error = SNMP_ERROR_GENERR;
489
	msg->sm_errorindex = msg->sm_i;
490
	return (-1);
491
}
492
493
void
494
snmpe_recvmsg(int fd, short sig, void *arg)
495
{
496
	struct snmpd		*env = arg;
497
	struct snmp_stats	*stats = &env->sc_stats;
498
	ssize_t			 len;
499
	struct snmp_message	*msg;
500
501
	if ((msg = calloc(1, sizeof(*msg))) == NULL)
502
		return;
503
504
	msg->sm_sock = fd;
505
	msg->sm_slen = sizeof(msg->sm_ss);
506
	if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
507
	    (struct sockaddr *)&msg->sm_ss, &msg->sm_slen,
508
	    (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) {
509
		free(msg);
510
		return;
511
	}
512
513
	stats->snmp_inpkts++;
514
	msg->sm_datalen = (size_t)len;
515
516
	bzero(&msg->sm_ber, sizeof(msg->sm_ber));
517
	msg->sm_ber.fd = -1;
518
	ber_set_application(&msg->sm_ber, smi_application);
519
	ber_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
520
521
	msg->sm_req = ber_read_elements(&msg->sm_ber, NULL);
522
	if (msg->sm_req == NULL) {
523
		stats->snmp_inasnparseerrs++;
524
		snmp_msgfree(msg);
525
		return;
526
	}
527
528
#ifdef DEBUG
529
	fprintf(stderr, "recv msg:\n");
530
	smi_debug_elements(msg->sm_req);
531
#endif
532
533
	if (snmpe_parse(msg) == -1) {
534
		if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
535
			usm_make_report(msg);
536
			snmpe_response(msg);
537
			return;
538
		} else {
539
			snmp_msgfree(msg);
540
			return;
541
		}
542
	}
543
544
	snmpe_dispatchmsg(msg);
545
}
546
547
void
548
snmpe_dispatchmsg(struct snmp_message *msg)
549
{
550
	if (snmpe_parsevarbinds(msg) == 1)
551
		return;
552
553
	/* not dispatched to subagent; respond directly */
554
	msg->sm_context = SNMP_C_GETRESP;
555
	snmpe_response(msg);
556
}
557
558
void
559
snmpe_response(struct snmp_message *msg)
560
{
561
	struct snmp_stats	*stats = &snmpd_env->sc_stats;
562
	u_int8_t		*ptr = NULL;
563
	ssize_t			 len;
564
565
	if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL)
566
		msg->sm_varbindresp = ber_unlink_elements(msg->sm_pduend);
567
568
	switch (msg->sm_error) {
569
	case SNMP_ERROR_TOOBIG:
570
		stats->snmp_intoobigs++;
571
		break;
572
	case SNMP_ERROR_NOSUCHNAME:
573
		stats->snmp_innosuchnames++;
574
		break;
575
	case SNMP_ERROR_BADVALUE:
576
		stats->snmp_inbadvalues++;
577
		break;
578
	case SNMP_ERROR_READONLY:
579
		stats->snmp_inreadonlys++;
580
		break;
581
	case SNMP_ERROR_GENERR:
582
	default:
583
		stats->snmp_ingenerrs++;
584
		break;
585
	}
586
587
	/* Create new SNMP packet */
588
	if (snmpe_encode(msg) < 0)
589
		goto done;
590
591
	len = ber_write_elements(&msg->sm_ber, msg->sm_resp);
592
	if (ber_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1)
593
		goto done;
594
595
	usm_finalize_digest(msg, ptr, len);
596
	len = sendtofrom(msg->sm_sock, ptr, len, 0,
597
	    (struct sockaddr *)&msg->sm_ss, msg->sm_slen,
598
	    (struct sockaddr *)&msg->sm_local_ss, msg->sm_local_slen);
599
	if (len != -1)
600
		stats->snmp_outpkts++;
601
602
 done:
603
	snmp_msgfree(msg);
604
}
605
606
void
607
snmp_msgfree(struct snmp_message *msg)
608
{
609
	ber_free(&msg->sm_ber);
610
	if (msg->sm_req != NULL)
611
		ber_free_elements(msg->sm_req);
612
	if (msg->sm_resp != NULL)
613
		ber_free_elements(msg->sm_resp);
614
	free(msg);
615
}
616
617
int
618
snmpe_encode(struct snmp_message *msg)
619
{
620
	struct ber_element	*ehdr;
621
	struct ber_element	*pdu, *epdu;
622
623
	msg->sm_resp = ber_add_sequence(NULL);
624
	if ((ehdr = ber_add_integer(msg->sm_resp, msg->sm_version)) == NULL)
625
		return -1;
626
	if (msg->sm_version == SNMP_V3) {
627
		char	f = MSG_SECLEVEL(msg);
628
629
		if ((ehdr = ber_printf_elements(ehdr, "{iixi}", msg->sm_msgid,
630
		    msg->sm_max_msg_size, &f, sizeof(f),
631
		    msg->sm_secmodel)) == NULL)
632
			return -1;
633
634
		/* XXX currently only USM supported */
635
		if ((ehdr = usm_encode(msg, ehdr)) == NULL)
636
			return -1;
637
	} else {
638
		if ((ehdr = ber_add_string(ehdr, msg->sm_community)) == NULL)
639
			return -1;
640
	}
641
642
	pdu = epdu = ber_add_sequence(NULL);
643
	if (msg->sm_version == SNMP_V3) {
644
		if ((epdu = ber_printf_elements(epdu, "xs{",
645
		    snmpd_env->sc_engineid, snmpd_env->sc_engineid_len,
646
		    msg->sm_ctxname)) == NULL) {
647
			ber_free_elements(pdu);
648
			return -1;
649
		}
650
	}
651
652
	if (!ber_printf_elements(epdu, "tiii{e}.", BER_CLASS_CONTEXT,
653
	    msg->sm_context, msg->sm_request,
654
	    msg->sm_error, msg->sm_errorindex,
655
	    msg->sm_varbindresp)) {
656
		ber_free_elements(pdu);
657
		return -1;
658
	}
659
660
	if (MSG_HAS_PRIV(msg))
661
		pdu = usm_encrypt(msg, pdu);
662
	ber_link_elements(ehdr, pdu);
663
664
#ifdef DEBUG
665
	fprintf(stderr, "resp msg:\n");
666
	smi_debug_elements(msg->sm_resp);
667
#endif
668
	return 0;
669
}