net-snmp
5.4.1
|
00001 #include <net-snmp/net-snmp-config.h> 00002 00003 #include <stdio.h> 00004 #include <sys/types.h> 00005 #include <ctype.h> 00006 #include <errno.h> 00007 00008 #if HAVE_STRING_H 00009 #include <string.h> 00010 #else 00011 #include <strings.h> 00012 #endif 00013 #if HAVE_STDLIB_H 00014 #include <stdlib.h> 00015 #endif 00016 #if HAVE_UNISTD_H 00017 #include <unistd.h> 00018 #endif 00019 #if HAVE_SYS_SOCKET_H 00020 #include <sys/socket.h> 00021 #endif 00022 #if HAVE_SYS_UN_H 00023 #include <sys/un.h> 00024 #endif 00025 00026 #if HAVE_DMALLOC_H 00027 #include <dmalloc.h> 00028 #endif 00029 00030 #include <net-snmp/types.h> 00031 #include <net-snmp/output_api.h> 00032 #include <net-snmp/config_api.h> 00033 00034 #include <net-snmp/library/snmp_transport.h> 00035 #include <net-snmp/library/snmpUDPDomain.h> 00036 #include <net-snmp/library/snmpUnixDomain.h> 00037 00038 00039 #ifndef NETSNMP_STREAM_QUEUE_LEN 00040 #define NETSNMP_STREAM_QUEUE_LEN 5 00041 #endif 00042 00043 #ifndef SUN_LEN 00044 /* 00045 * Evaluate to actual length of the `sockaddr_un' structure. 00046 */ 00047 #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ 00048 + strlen ((ptr)->sun_path)) 00049 #endif 00050 00051 oid netsnmp_UnixDomain[] = { TRANSPORT_DOMAIN_LOCAL }; 00052 static netsnmp_tdomain unixDomain; 00053 00054 00055 /* 00056 * This is the structure we use to hold transport-specific data. 00057 */ 00058 00059 typedef struct _sockaddr_un_pair { 00060 int local; 00061 struct sockaddr_un server; 00062 struct sockaddr_un client; 00063 } sockaddr_un_pair; 00064 00065 00066 /* 00067 * Return a string representing the address in data, or else the "far end" 00068 * address if data is NULL. 00069 */ 00070 00071 static char * 00072 netsnmp_unix_fmtaddr(netsnmp_transport *t, void *data, int len) 00073 { 00074 struct sockaddr_un *to = NULL; 00075 00076 if (data != NULL) { 00077 to = (struct sockaddr_un *) data; 00078 } else if (t != NULL && t->data != NULL) { 00079 to = &(((sockaddr_un_pair *) t->data)->server); 00080 len = SUN_LEN(to); 00081 } 00082 if (to == NULL) { 00083 /* 00084 * "Local IPC" is the Posix.1g term for Unix domain protocols, 00085 * according to W. R. Stevens, ``Unix Network Programming Volume I 00086 * Second Edition'', p. 374. 00087 */ 00088 return strdup("Local IPC: unknown"); 00089 } else if (to->sun_path[0] == 0) { 00090 /* 00091 * This is an abstract name. We could render it as hex or something 00092 * but let's not worry about that for now. 00093 */ 00094 return strdup("Local IPC: abstract"); 00095 } else { 00096 char *tmp = (char *) malloc(16 + len); 00097 if (tmp != NULL) { 00098 sprintf(tmp, "Local IPC: %s", to->sun_path); 00099 } 00100 return tmp; 00101 } 00102 } 00103 00104 00105 00106 /* 00107 * You can write something into opaque that will subsequently get passed back 00108 * to your send function if you like. For instance, you might want to 00109 * remember where a PDU came from, so that you can send a reply there... 00110 */ 00111 00112 static int 00113 netsnmp_unix_recv(netsnmp_transport *t, void *buf, int size, 00114 void **opaque, int *olength) 00115 { 00116 int rc = -1; 00117 socklen_t tolen = sizeof(struct sockaddr_un); 00118 struct sockaddr *to; 00119 00120 00121 if (t != NULL && t->sock >= 0) { 00122 to = (struct sockaddr *) malloc(sizeof(struct sockaddr_un)); 00123 if (to == NULL) { 00124 *opaque = NULL; 00125 *olength = 0; 00126 return -1; 00127 } else { 00128 memset(to, 0, tolen); 00129 } 00130 if(getsockname(t->sock, to, &tolen) != 0){ 00131 free(to); 00132 *opaque = NULL; 00133 *olength = 0; 00134 return -1; 00135 }; 00136 while (rc < 0) { 00137 rc = recv(t->sock, buf, size, 0); 00138 if (rc < 0 && errno != EINTR) { 00139 DEBUGMSGTL(("netsnmp_unix", "recv fd %d err %d (\"%s\")\n", 00140 t->sock, errno, strerror(errno))); 00141 return rc; 00142 } 00143 *opaque = (void*)to; 00144 *olength = sizeof(struct sockaddr_un); 00145 } 00146 DEBUGMSGTL(("netsnmp_unix", "recv fd %d got %d bytes\n", t->sock, rc)); 00147 } 00148 return rc; 00149 } 00150 00151 00152 00153 static int 00154 netsnmp_unix_send(netsnmp_transport *t, void *buf, int size, 00155 void **opaque, int *olength) 00156 { 00157 int rc = -1; 00158 00159 if (t != NULL && t->sock >= 0) { 00160 DEBUGMSGTL(("netsnmp_unix", "send %d bytes to %p on fd %d\n", 00161 size, buf, t->sock)); 00162 while (rc < 0) { 00163 rc = send(t->sock, buf, size, 0); 00164 if (rc < 0 && errno != EINTR) { 00165 break; 00166 } 00167 } 00168 } 00169 return rc; 00170 } 00171 00172 00173 00174 static int 00175 netsnmp_unix_close(netsnmp_transport *t) 00176 { 00177 int rc = 0; 00178 sockaddr_un_pair *sup = (sockaddr_un_pair *) t->data; 00179 00180 if (t->sock >= 0) { 00181 #ifndef HAVE_CLOSESOCKET 00182 rc = close(t->sock); 00183 #else 00184 rc = closesocket(t->sock); 00185 #endif 00186 t->sock = -1; 00187 if (sup != NULL) { 00188 if (sup->local) { 00189 if (sup->server.sun_path[0] != 0) { 00190 DEBUGMSGTL(("netsnmp_unix", "close: server unlink(\"%s\")\n", 00191 sup->server.sun_path)); 00192 unlink(sup->server.sun_path); 00193 } 00194 } else { 00195 if (sup->client.sun_path[0] != 0) { 00196 DEBUGMSGTL(("netsnmp_unix", "close: client unlink(\"%s\")\n", 00197 sup->client.sun_path)); 00198 unlink(sup->client.sun_path); 00199 } 00200 } 00201 } 00202 return rc; 00203 } else { 00204 return -1; 00205 } 00206 } 00207 00208 00209 00210 static int 00211 netsnmp_unix_accept(netsnmp_transport *t) 00212 { 00213 struct sockaddr *farend = NULL; 00214 int newsock = -1; 00215 socklen_t farendlen = sizeof(struct sockaddr_un); 00216 00217 farend = (struct sockaddr *) malloc(farendlen); 00218 00219 if (farend == NULL) { 00220 /* 00221 * Indicate that the acceptance of this socket failed. 00222 */ 00223 DEBUGMSGTL(("netsnmp_unix", "accept: malloc failed\n")); 00224 return -1; 00225 } 00226 memset(farend, 0, farendlen); 00227 00228 if (t != NULL && t->sock >= 0) { 00229 newsock = accept(t->sock, farend, &farendlen); 00230 00231 if (newsock < 0) { 00232 DEBUGMSGTL(("netsnmp_unix","accept failed rc %d errno %d \"%s\"\n", 00233 newsock, errno, strerror(errno))); 00234 free(farend); 00235 return newsock; 00236 } 00237 00238 if (t->data != NULL) { 00239 free(t->data); 00240 } 00241 00242 DEBUGMSGTL(("netsnmp_unix", "accept succeeded (farend %p len %d)\n", 00243 farend, farendlen)); 00244 t->data = farend; 00245 t->data_length = sizeof(struct sockaddr_un); 00246 netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0); 00247 netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0); 00248 return newsock; 00249 } else { 00250 free(farend); 00251 return -1; 00252 } 00253 } 00254 00255 00256 00257 /* 00258 * Open a Unix-domain transport for SNMP. Local is TRUE if addr is the local 00259 * address to bind to (i.e. this is a server-type session); otherwise addr is 00260 * the remote address to send things to (and we make up a temporary name for 00261 * the local end of the connection). 00262 */ 00263 00264 netsnmp_transport * 00265 netsnmp_unix_transport(struct sockaddr_un *addr, int local) 00266 { 00267 netsnmp_transport *t = NULL; 00268 sockaddr_un_pair *sup = NULL; 00269 int rc = 0; 00270 char *string = NULL; 00271 00272 if (addr == NULL || addr->sun_family != AF_UNIX) { 00273 return NULL; 00274 } 00275 00276 t = (netsnmp_transport *) malloc(sizeof(netsnmp_transport)); 00277 if (t == NULL) { 00278 return NULL; 00279 } 00280 00281 string = netsnmp_unix_fmtaddr(NULL, (void *)addr, 00282 sizeof(struct sockaddr_un)); 00283 DEBUGMSGTL(("netsnmp_unix", "open %s %s\n", local ? "local" : "remote", 00284 string)); 00285 free(string); 00286 00287 memset(t, 0, sizeof(netsnmp_transport)); 00288 00289 t->domain = netsnmp_UnixDomain; 00290 t->domain_length = 00291 sizeof(netsnmp_UnixDomain) / sizeof(netsnmp_UnixDomain[0]); 00292 00293 t->data = malloc(sizeof(sockaddr_un_pair)); 00294 if (t->data == NULL) { 00295 netsnmp_transport_free(t); 00296 return NULL; 00297 } 00298 memset(t->data, 0, sizeof(sockaddr_un_pair)); 00299 t->data_length = sizeof(sockaddr_un_pair); 00300 sup = (sockaddr_un_pair *) t->data; 00301 00302 t->sock = socket(PF_UNIX, SOCK_STREAM, 0); 00303 if (t->sock < 0) { 00304 netsnmp_transport_free(t); 00305 return NULL; 00306 } 00307 00308 t->flags = NETSNMP_TRANSPORT_FLAG_STREAM; 00309 00310 if (local) { 00311 t->local = (u_char *)malloc(strlen(addr->sun_path)); 00312 if (t->local == NULL) { 00313 netsnmp_transport_free(t); 00314 return NULL; 00315 } 00316 memcpy(t->local, addr->sun_path, strlen(addr->sun_path)); 00317 t->local_length = strlen(addr->sun_path); 00318 00319 /* 00320 * This session is inteneded as a server, so we must bind to the given 00321 * path (unlinking it first, to avoid errors). 00322 */ 00323 00324 t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; 00325 00326 unlink(addr->sun_path); 00327 rc = bind(t->sock, (struct sockaddr *) addr, SUN_LEN(addr)); 00328 if (rc != 0) { 00329 DEBUGMSGTL(("netsnmp_unix_transport", 00330 "couldn't bind \"%s\", errno %d (%s)\n", 00331 addr->sun_path, errno, strerror(errno))); 00332 netsnmp_unix_close(t); 00333 netsnmp_transport_free(t); 00334 return NULL; 00335 } 00336 00337 /* 00338 * Save the address in the transport-specific data pointer for later 00339 * use by netsnmp_unix_close. 00340 */ 00341 00342 sup->server.sun_family = AF_UNIX; 00343 strcpy(sup->server.sun_path, addr->sun_path); 00344 sup->local = 1; 00345 00346 /* 00347 * Now sit here and listen for connections to arrive. 00348 */ 00349 00350 rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN); 00351 if (rc != 0) { 00352 DEBUGMSGTL(("netsnmp_unix_transport", 00353 "couldn't listen to \"%s\", errno %d (%s)\n", 00354 addr->sun_path, errno, strerror(errno))); 00355 netsnmp_unix_close(t); 00356 netsnmp_transport_free(t); 00357 return NULL; 00358 } 00359 00360 } else { 00361 t->remote = (u_char *)malloc(strlen(addr->sun_path)); 00362 if (t->remote == NULL) { 00363 netsnmp_transport_free(t); 00364 return NULL; 00365 } 00366 memcpy(t->remote, addr->sun_path, strlen(addr->sun_path)); 00367 t->remote_length = strlen(addr->sun_path); 00368 00369 rc = connect(t->sock, (struct sockaddr *) addr, 00370 sizeof(struct sockaddr_un)); 00371 if (rc != 0) { 00372 DEBUGMSGTL(("netsnmp_unix_transport", 00373 "couldn't connect to \"%s\", errno %d (%s)\n", 00374 addr->sun_path, errno, strerror(errno))); 00375 netsnmp_unix_close(t); 00376 netsnmp_transport_free(t); 00377 return NULL; 00378 } 00379 00380 /* 00381 * Save the remote address in the transport-specific data pointer for 00382 * later use by netsnmp_unix_send. 00383 */ 00384 00385 sup->server.sun_family = AF_UNIX; 00386 strcpy(sup->server.sun_path, addr->sun_path); 00387 sup->local = 0; 00388 netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0); 00389 netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0); 00390 } 00391 00392 /* 00393 * Message size is not limited by this transport (hence msgMaxSize 00394 * is equal to the maximum legal size of an SNMP message). 00395 */ 00396 00397 t->msgMaxSize = 0x7fffffff; 00398 t->f_recv = netsnmp_unix_recv; 00399 t->f_send = netsnmp_unix_send; 00400 t->f_close = netsnmp_unix_close; 00401 t->f_accept = netsnmp_unix_accept; 00402 t->f_fmtaddr = netsnmp_unix_fmtaddr; 00403 00404 return t; 00405 } 00406 00407 netsnmp_transport * 00408 netsnmp_unix_create_tstring(const char *string, int local, 00409 const char *default_target) 00410 { 00411 struct sockaddr_un addr; 00412 00413 if (string && *string != '\0') { 00414 } else if (default_target && *default_target != '\0') { 00415 string = default_target; 00416 } 00417 00418 if ((string != NULL && *string != '\0') && 00419 (strlen(string) < sizeof(addr.sun_path))) { 00420 addr.sun_family = AF_UNIX; 00421 memset(addr.sun_path, 0, sizeof(addr.sun_path)); 00422 strncpy(addr.sun_path, string, sizeof(addr.sun_path) - 1); 00423 return netsnmp_unix_transport(&addr, local); 00424 } else { 00425 if (string != NULL && *string != '\0') { 00426 snmp_log(LOG_ERR, "Path too long for Unix domain transport\n"); 00427 } 00428 return NULL; 00429 } 00430 } 00431 00432 00433 00434 netsnmp_transport * 00435 netsnmp_unix_create_ostring(const u_char * o, size_t o_len, int local) 00436 { 00437 struct sockaddr_un addr; 00438 00439 if (o_len > 0 && o_len < (sizeof(addr.sun_path) - 1)) { 00440 addr.sun_family = AF_UNIX; 00441 memset(addr.sun_path, 0, sizeof(addr.sun_path)); 00442 strncpy(addr.sun_path, (const char *)o, o_len); 00443 return netsnmp_unix_transport(&addr, local); 00444 } else { 00445 if (o_len > 0) { 00446 snmp_log(LOG_ERR, "Path too long for Unix domain transport\n"); 00447 } 00448 } 00449 return NULL; 00450 } 00451 00452 00453 00454 void 00455 netsnmp_unix_ctor(void) 00456 { 00457 unixDomain.name = netsnmp_UnixDomain; 00458 unixDomain.name_length = sizeof(netsnmp_UnixDomain) / sizeof(oid); 00459 unixDomain.prefix = (const char**)calloc(2, sizeof(char *)); 00460 unixDomain.prefix[0] = "unix"; 00461 00462 unixDomain.f_create_from_tstring_new = netsnmp_unix_create_tstring; 00463 unixDomain.f_create_from_ostring = netsnmp_unix_create_ostring; 00464 00465 netsnmp_tdomain_register(&unixDomain); 00466 } 00467 00468 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) 00469 /* support for SNMPv1 and SNMPv2c on unix domain*/ 00470 00471 #define EXAMPLE_COMMUNITY "COMMUNITY" 00472 typedef struct _com2SecUnixEntry { 00473 char community[COMMUNITY_MAX_LEN]; 00474 char sockpath[sizeof(struct sockaddr_un)]; 00475 unsigned long pathlen; 00476 char secName[VACMSTRINGLEN]; 00477 char contextName[VACMSTRINGLEN]; 00478 struct _com2SecUnixEntry *next; 00479 } com2SecUnixEntry; 00480 00481 com2SecUnixEntry *com2SecUnixList = NULL, *com2SecUnixListLast = NULL; 00482 00483 00484 int 00485 netsnmp_unix_getSecName(void *opaque, int olength, 00486 const char *community, 00487 size_t community_len, 00488 char **secName, char **contextName) 00489 { 00490 com2SecUnixEntry *c; 00491 struct sockaddr_un *to = (struct sockaddr_un *) opaque; 00492 char *ztcommunity = NULL; 00493 00494 if (secName != NULL) { 00495 *secName = NULL; /* Haven't found anything yet */ 00496 } 00497 00498 /* 00499 * Special case if there are NO entries (as opposed to no MATCHING 00500 * entries). 00501 */ 00502 00503 if (com2SecUnixList == NULL) { 00504 DEBUGMSGTL(("netsnmp_unix_getSecName", "no com2sec entries\n")); 00505 return 0; 00506 } 00507 00508 /* 00509 * If there is no unix socket path, then there can be no valid security 00510 * name. 00511 */ 00512 00513 if (opaque == NULL || olength != sizeof(struct sockaddr_un) || 00514 to->sun_family != AF_UNIX) { 00515 DEBUGMSGTL(("netsnmp_unix_getSecName", 00516 "no unix destine address in PDU?\n")); 00517 return 1; 00518 } 00519 00520 DEBUGIF("netsnmp_unix_getSecName") { 00521 ztcommunity = (char *)malloc(community_len + 1); 00522 if (ztcommunity != NULL) { 00523 memcpy(ztcommunity, community, community_len); 00524 ztcommunity[community_len] = '\0'; 00525 } 00526 00527 DEBUGMSGTL(("netsnmp_unix_getSecName", "resolve <\"%s\">\n", 00528 ztcommunity ? ztcommunity : "<malloc error>")); 00529 } 00530 00531 for (c = com2SecUnixList; c != NULL; c = c->next) { 00532 DEBUGMSGTL(("netsnmp_unix_getSecName","compare <\"%s\",to socket %s>", 00533 c->community, c->sockpath )); 00534 if ((community_len == strlen(c->community)) && 00535 (memcmp(community, c->community, community_len) == 0) && 00536 /* compare sockpath, if pathlen == 0, always match */ 00537 (strlen(to->sun_path) == c->pathlen || c->pathlen == 0) && 00538 (memcmp(to->sun_path, c->sockpath, c->pathlen) == 0) 00539 ) { 00540 DEBUGMSG(("netsnmp_unix_getSecName", "... SUCCESS\n")); 00541 if (secName != NULL) { 00542 *secName = c->secName; 00543 *contextName = c->contextName; 00544 } 00545 break; 00546 } 00547 DEBUGMSG(("netsnmp_unix_getSecName", "... nope\n")); 00548 } 00549 if (ztcommunity != NULL) { 00550 free(ztcommunity); 00551 } 00552 return 1; 00553 } 00554 00555 void 00556 netsnmp_unix_parse_security(const char *token, char *param) 00557 { 00558 char secName[VACMSTRINGLEN + 1], community[COMMUNITY_MAX_LEN + 1]; 00559 char contextName[VACMSTRINGLEN + 1]; 00560 char sockpath[sizeof(struct sockaddr_un) + 1]; 00561 com2SecUnixEntry *e = NULL; 00562 00563 00564 param = copy_nword(param, secName, VACMSTRINGLEN); 00565 if (strcmp(secName, "-Cn") == 0) { 00566 if (!secName) { 00567 config_perror("missing CONTEXT_NAME parameter"); 00568 return; 00569 } 00570 param = copy_nword( param, contextName, sizeof(contextName)); 00571 param = copy_nword( param, secName, sizeof(secName)); 00572 } else { 00573 contextName[0] = '\0'; 00574 } 00575 if (secName[0] == '\0') { 00576 config_perror("missing NAME parameter"); 00577 return; 00578 } else if (strlen(secName) > (VACMSTRINGLEN - 1)) { 00579 config_perror("security name too long"); 00580 return; 00581 } 00582 00583 param = copy_nword(param, sockpath, sizeof(struct sockaddr_un) - 1); 00584 if (sockpath[0] == '\0') { 00585 config_perror("missing SOCKPATH parameter"); 00586 return; 00587 } else if (strlen(sockpath) > (sizeof(struct sockaddr_un) - 1)) { 00588 config_perror("sockpath too long"); 00589 return; 00590 } 00591 /* if sockpath == "default", set pathlen=0*/ 00592 if(strcmp(sockpath, "default") == 0){ 00593 sockpath[0] = 0; 00594 } 00595 00596 param = copy_nword(param, community, COMMUNITY_MAX_LEN); 00597 if (community[0] == '\0') { 00598 config_perror("missing COMMUNITY parameter\n"); 00599 return; 00600 } else if (strncmp 00601 (community, EXAMPLE_COMMUNITY, strlen(EXAMPLE_COMMUNITY)) 00602 == 0) { 00603 config_perror("example config COMMUNITY not properly configured"); 00604 return; 00605 } else if (strlen(community) > (COMMUNITY_MAX_LEN - 1)) { 00606 config_perror("community name too long"); 00607 return; 00608 } 00609 00610 e = (com2SecUnixEntry *) malloc(sizeof(com2SecUnixEntry)); 00611 if (e == NULL) { 00612 config_perror("memory error"); 00613 return; 00614 } 00615 00616 DEBUGMSGTL(("netsnmp_unix_parse_security", 00617 "<\"%s\"> => \"%s\"\n", community, secName)); 00618 00619 strcpy(e->secName, secName); 00620 strcpy(e->contextName, contextName); 00621 strcpy(e->community, community); 00622 strcpy(e->sockpath, sockpath); 00623 e->pathlen = strlen(sockpath); 00624 e->next = NULL; 00625 00626 if (com2SecUnixListLast != NULL) { 00627 com2SecUnixListLast->next = e; 00628 com2SecUnixListLast = e; 00629 } else { 00630 com2SecUnixListLast = com2SecUnixList = e; 00631 } 00632 } 00633 00634 void 00635 netsnmp_unix_com2SecList_free(void) 00636 { 00637 com2SecUnixEntry *e = com2SecUnixList; 00638 while (e != NULL) { 00639 com2SecUnixEntry *tmp = e; 00640 e = e->next; 00641 free(tmp); 00642 } 00643 com2SecUnixList = com2SecUnixListLast = NULL; 00644 } 00645 #endif /* support for community based SNMP */ 00646 00647 void 00648 netsnmp_unix_agent_config_tokens_register(void) 00649 { 00650 #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) 00651 register_app_config_handler("com2secunix", netsnmp_unix_parse_security, 00652 netsnmp_unix_com2SecList_free, 00653 "[-Cn CONTEXT] secName sockpath community"); 00654 #endif /* support for community based SNMP */ 00655 }