tsocket: fill in sa.sa_len if the system supports it
[Samba/gebeck_regimport.git] / nsswitch / libwbclient / wbc_sid.c
blob73bd4162472c1f3cfbfb1d29122807f8f24201ca
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind client API
6 Copyright (C) Gerald (Jerry) Carter 2007
7 Copyright (C) Volker Lendecke 2010
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /* Required Headers */
26 #include "replace.h"
27 #include "libwbclient.h"
28 #include "../winbind_client.h"
30 /* Convert a binary SID to a character string */
31 wbcErr wbcSidToString(const struct wbcDomainSid *sid,
32 char **sid_string)
34 uint32_t id_auth;
35 int i, ofs, maxlen;
36 char *result;
38 if (!sid) {
39 return WBC_ERR_INVALID_SID;
42 maxlen = sid->num_auths * 11 + 25;
44 result = (char *)wbcAllocateMemory(maxlen, 1, NULL);
45 if (result == NULL) {
46 return WBC_ERR_NO_MEMORY;
50 * BIG NOTE: this function only does SIDS where the identauth is not
51 * >= ^32 in a range of 2^48.
54 id_auth = sid->id_auth[5] +
55 (sid->id_auth[4] << 8) +
56 (sid->id_auth[3] << 16) +
57 (sid->id_auth[2] << 24);
59 ofs = snprintf(result, maxlen, "S-%u-%lu",
60 (unsigned int)sid->sid_rev_num, (unsigned long)id_auth);
62 for (i = 0; i < sid->num_auths; i++) {
63 ofs += snprintf(result + ofs, maxlen - ofs, "-%lu",
64 (unsigned long)sid->sub_auths[i]);
67 *sid_string = result;
68 return WBC_ERR_SUCCESS;
71 /* Convert a character string to a binary SID */
72 wbcErr wbcStringToSid(const char *str,
73 struct wbcDomainSid *sid)
75 const char *p;
76 char *q;
77 uint32_t x;
78 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
80 if (!sid) {
81 wbc_status = WBC_ERR_INVALID_PARAM;
82 BAIL_ON_WBC_ERROR(wbc_status);
85 /* Sanity check for either "S-" or "s-" */
87 if (!str
88 || (str[0]!='S' && str[0]!='s')
89 || (str[1]!='-'))
91 wbc_status = WBC_ERR_INVALID_PARAM;
92 BAIL_ON_WBC_ERROR(wbc_status);
95 /* Get the SID revision number */
97 p = str+2;
98 x = (uint32_t)strtol(p, &q, 10);
99 if (x==0 || !q || *q!='-') {
100 wbc_status = WBC_ERR_INVALID_SID;
101 BAIL_ON_WBC_ERROR(wbc_status);
103 sid->sid_rev_num = (uint8_t)x;
105 /* Next the Identifier Authority. This is stored in big-endian
106 in a 6 byte array. */
108 p = q+1;
109 x = (uint32_t)strtol(p, &q, 10);
110 if (!q || *q!='-') {
111 wbc_status = WBC_ERR_INVALID_SID;
112 BAIL_ON_WBC_ERROR(wbc_status);
114 sid->id_auth[5] = (x & 0x000000ff);
115 sid->id_auth[4] = (x & 0x0000ff00) >> 8;
116 sid->id_auth[3] = (x & 0x00ff0000) >> 16;
117 sid->id_auth[2] = (x & 0xff000000) >> 24;
118 sid->id_auth[1] = 0;
119 sid->id_auth[0] = 0;
121 /* now read the the subauthorities */
123 p = q +1;
124 sid->num_auths = 0;
125 while (sid->num_auths < WBC_MAXSUBAUTHS) {
126 x=(uint32_t)strtoul(p, &q, 10);
127 if (p == q)
128 break;
129 if (q == NULL) {
130 wbc_status = WBC_ERR_INVALID_SID;
131 BAIL_ON_WBC_ERROR(wbc_status);
133 sid->sub_auths[sid->num_auths++] = x;
135 if ((*q!='-') || (*q=='\0'))
136 break;
137 p = q + 1;
140 /* IF we ended early, then the SID could not be converted */
142 if (q && *q!='\0') {
143 wbc_status = WBC_ERR_INVALID_SID;
144 BAIL_ON_WBC_ERROR(wbc_status);
147 wbc_status = WBC_ERR_SUCCESS;
149 done:
150 return wbc_status;
155 /* Convert a domain and name to SID */
156 wbcErr wbcLookupName(const char *domain,
157 const char *name,
158 struct wbcDomainSid *sid,
159 enum wbcSidType *name_type)
161 struct winbindd_request request;
162 struct winbindd_response response;
163 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
165 if (!sid || !name_type) {
166 wbc_status = WBC_ERR_INVALID_PARAM;
167 BAIL_ON_WBC_ERROR(wbc_status);
170 /* Initialize request */
172 ZERO_STRUCT(request);
173 ZERO_STRUCT(response);
175 /* dst is already null terminated from the memset above */
177 strncpy(request.data.name.dom_name, domain,
178 sizeof(request.data.name.dom_name)-1);
179 strncpy(request.data.name.name, name,
180 sizeof(request.data.name.name)-1);
182 wbc_status = wbcRequestResponse(WINBINDD_LOOKUPNAME,
183 &request,
184 &response);
185 BAIL_ON_WBC_ERROR(wbc_status);
187 wbc_status = wbcStringToSid(response.data.sid.sid, sid);
188 BAIL_ON_WBC_ERROR(wbc_status);
190 *name_type = (enum wbcSidType)response.data.sid.type;
192 wbc_status = WBC_ERR_SUCCESS;
194 done:
195 return wbc_status;
199 /* Convert a SID to a domain and name */
200 wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
201 char **pdomain,
202 char **pname,
203 enum wbcSidType *pname_type)
205 struct winbindd_request request;
206 struct winbindd_response response;
207 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
208 char *sid_string = NULL;
209 char *domain, *name;
211 if (!sid) {
212 return WBC_ERR_INVALID_PARAM;
215 /* Initialize request */
217 ZERO_STRUCT(request);
218 ZERO_STRUCT(response);
220 /* dst is already null terminated from the memset above */
222 wbc_status = wbcSidToString(sid, &sid_string);
223 if (!WBC_ERROR_IS_OK(wbc_status)) {
224 return wbc_status;
227 strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
228 wbcFreeMemory(sid_string);
230 /* Make request */
232 wbc_status = wbcRequestResponse(WINBINDD_LOOKUPSID, &request,
233 &response);
234 if (!WBC_ERROR_IS_OK(wbc_status)) {
235 return wbc_status;
238 /* Copy out result */
240 wbc_status = WBC_ERR_NO_MEMORY;
241 domain = NULL;
242 name = NULL;
244 domain = wbcStrDup(response.data.name.dom_name);
245 if (domain == NULL) {
246 goto done;
248 name = wbcStrDup(response.data.name.name);
249 if (name == NULL) {
250 goto done;
252 if (pdomain != NULL) {
253 *pdomain = domain;
254 domain = NULL;
256 if (pname != NULL) {
257 *pname = name;
258 name = NULL;
260 if (pname_type != NULL) {
261 *pname_type = (enum wbcSidType)response.data.name.type;
263 wbc_status = WBC_ERR_SUCCESS;
264 done:
265 wbcFreeMemory(name);
266 wbcFreeMemory(domain);
267 return wbc_status;
270 /* Translate a collection of RIDs within a domain to names */
272 wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
273 int num_rids,
274 uint32_t *rids,
275 const char **pp_domain_name,
276 const char ***pnames,
277 enum wbcSidType **ptypes)
279 size_t i, len, ridbuf_size;
280 char *ridlist;
281 char *p;
282 struct winbindd_request request;
283 struct winbindd_response response;
284 char *sid_string = NULL;
285 char *domain_name = NULL;
286 const char **names = NULL;
287 enum wbcSidType *types = NULL;
288 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
290 /* Initialise request */
292 ZERO_STRUCT(request);
293 ZERO_STRUCT(response);
295 if (!dom_sid || (num_rids == 0)) {
296 wbc_status = WBC_ERR_INVALID_PARAM;
297 BAIL_ON_WBC_ERROR(wbc_status);
300 wbc_status = wbcSidToString(dom_sid, &sid_string);
301 BAIL_ON_WBC_ERROR(wbc_status);
303 strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
304 wbcFreeMemory(sid_string);
306 /* Even if all the Rids were of maximum 32bit values,
307 we would only have 11 bytes per rid in the final array
308 ("4294967296" + \n). Add one more byte for the
309 terminating '\0' */
311 ridbuf_size = (sizeof(char)*11) * num_rids + 1;
313 ridlist = (char *)malloc(ridbuf_size);
314 BAIL_ON_PTR_ERROR(ridlist, wbc_status);
316 len = 0;
317 for (i=0; i<num_rids; i++) {
318 len += snprintf(ridlist + len, ridbuf_size - len, "%u\n",
319 rids[i]);
321 ridlist[len] = '\0';
322 len += 1;
324 request.extra_data.data = ridlist;
325 request.extra_len = len;
327 wbc_status = wbcRequestResponse(WINBINDD_LOOKUPRIDS,
328 &request,
329 &response);
330 free(ridlist);
331 BAIL_ON_WBC_ERROR(wbc_status);
333 domain_name = wbcStrDup(response.data.domain_name);
334 BAIL_ON_PTR_ERROR(domain_name, wbc_status);
336 names = wbcAllocateStringArray(num_rids);
337 BAIL_ON_PTR_ERROR(names, wbc_status);
339 types = (enum wbcSidType *)wbcAllocateMemory(
340 num_rids, sizeof(enum wbcSidType), NULL);
341 BAIL_ON_PTR_ERROR(types, wbc_status);
343 p = (char *)response.extra_data.data;
345 for (i=0; i<num_rids; i++) {
346 char *q;
348 if (*p == '\0') {
349 wbc_status = WBC_ERR_INVALID_RESPONSE;
350 goto done;
353 types[i] = (enum wbcSidType)strtoul(p, &q, 10);
355 if (*q != ' ') {
356 wbc_status = WBC_ERR_INVALID_RESPONSE;
357 goto done;
360 p = q+1;
362 if ((q = strchr(p, '\n')) == NULL) {
363 wbc_status = WBC_ERR_INVALID_RESPONSE;
364 goto done;
367 *q = '\0';
369 names[i] = strdup(p);
370 BAIL_ON_PTR_ERROR(names[i], wbc_status);
372 p = q+1;
375 if (*p != '\0') {
376 wbc_status = WBC_ERR_INVALID_RESPONSE;
377 goto done;
380 wbc_status = WBC_ERR_SUCCESS;
382 done:
383 winbindd_free_response(&response);
385 if (WBC_ERROR_IS_OK(wbc_status)) {
386 *pp_domain_name = domain_name;
387 *pnames = names;
388 *ptypes = types;
390 else {
391 wbcFreeMemory(domain_name);
392 wbcFreeMemory(names);
393 wbcFreeMemory(types);
396 return wbc_status;
399 /* Get the groups a user belongs to */
400 wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
401 bool domain_groups_only,
402 uint32_t *num_sids,
403 struct wbcDomainSid **_sids)
405 uint32_t i;
406 const char *s;
407 struct winbindd_request request;
408 struct winbindd_response response;
409 char *sid_string = NULL;
410 struct wbcDomainSid *sids = NULL;
411 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
412 int cmd;
414 /* Initialise request */
416 ZERO_STRUCT(request);
417 ZERO_STRUCT(response);
419 if (!user_sid) {
420 wbc_status = WBC_ERR_INVALID_PARAM;
421 BAIL_ON_WBC_ERROR(wbc_status);
424 wbc_status = wbcSidToString(user_sid, &sid_string);
425 BAIL_ON_WBC_ERROR(wbc_status);
427 strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
428 wbcFreeMemory(sid_string);
430 if (domain_groups_only) {
431 cmd = WINBINDD_GETUSERDOMGROUPS;
432 } else {
433 cmd = WINBINDD_GETUSERSIDS;
436 wbc_status = wbcRequestResponse(cmd,
437 &request,
438 &response);
439 BAIL_ON_WBC_ERROR(wbc_status);
441 if (response.data.num_entries &&
442 !response.extra_data.data) {
443 wbc_status = WBC_ERR_INVALID_RESPONSE;
444 BAIL_ON_WBC_ERROR(wbc_status);
447 sids = (struct wbcDomainSid *)wbcAllocateMemory(
448 response.data.num_entries, sizeof(struct wbcDomainSid),
449 NULL);
450 BAIL_ON_PTR_ERROR(sids, wbc_status);
452 s = (const char *)response.extra_data.data;
453 for (i = 0; i < response.data.num_entries; i++) {
454 char *n = strchr(s, '\n');
455 if (n) {
456 *n = '\0';
458 wbc_status = wbcStringToSid(s, &sids[i]);
459 BAIL_ON_WBC_ERROR(wbc_status);
460 s += strlen(s) + 1;
463 *num_sids = response.data.num_entries;
464 *_sids = sids;
465 sids = NULL;
466 wbc_status = WBC_ERR_SUCCESS;
468 done:
469 winbindd_free_response(&response);
470 if (sids) {
471 wbcFreeMemory(sids);
474 return wbc_status;
477 static inline
478 wbcErr _sid_to_rid(struct wbcDomainSid *sid, uint32_t *rid)
480 if (sid->num_auths < 1) {
481 return WBC_ERR_INVALID_RESPONSE;
483 *rid = sid->sub_auths[sid->num_auths - 1];
485 return WBC_ERR_SUCCESS;
488 /* Get alias membership for sids */
489 wbcErr wbcGetSidAliases(const struct wbcDomainSid *dom_sid,
490 struct wbcDomainSid *sids,
491 uint32_t num_sids,
492 uint32_t **alias_rids,
493 uint32_t *num_alias_rids)
495 uint32_t i;
496 const char *s;
497 struct winbindd_request request;
498 struct winbindd_response response;
499 char *sid_string = NULL;
500 ssize_t sid_len;
501 ssize_t extra_data_len = 0;
502 char * extra_data = NULL;
503 ssize_t buflen = 0;
504 struct wbcDomainSid sid;
505 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
506 uint32_t * rids = NULL;
508 /* Initialise request */
510 ZERO_STRUCT(request);
511 ZERO_STRUCT(response);
513 if (!dom_sid) {
514 wbc_status = WBC_ERR_INVALID_PARAM;
515 goto done;
518 wbc_status = wbcSidToString(dom_sid, &sid_string);
519 BAIL_ON_WBC_ERROR(wbc_status);
521 strncpy(request.data.sid, sid_string, sizeof(request.data.sid)-1);
522 wbcFreeMemory(sid_string);
523 sid_string = NULL;
525 /* Lets assume each sid is around 57 characters
526 * S-1-5-21-AAAAAAAAAAA-BBBBBBBBBBB-CCCCCCCCCCC-DDDDDDDDDDD\n */
527 buflen = 57 * num_sids;
528 extra_data = (char *)malloc(buflen);
529 if (!extra_data) {
530 wbc_status = WBC_ERR_NO_MEMORY;
531 goto done;
534 /* Build the sid list */
535 for (i=0; i<num_sids; i++) {
536 wbc_status = wbcSidToString(&sids[i], &sid_string);
537 BAIL_ON_WBC_ERROR(wbc_status);
539 sid_len = strlen(sid_string);
541 if (buflen < extra_data_len + sid_len + 2) {
542 buflen *= 2;
543 extra_data = (char *)realloc(extra_data, buflen);
544 if (!extra_data) {
545 wbc_status = WBC_ERR_NO_MEMORY;
546 BAIL_ON_WBC_ERROR(wbc_status);
550 strncpy(&extra_data[extra_data_len], sid_string,
551 buflen - extra_data_len);
552 extra_data_len += sid_len;
553 extra_data[extra_data_len++] = '\n';
554 extra_data[extra_data_len] = '\0';
555 wbcFreeMemory(sid_string);
556 sid_string = NULL;
558 extra_data_len += 1;
560 request.extra_data.data = extra_data;
561 request.extra_len = extra_data_len;
563 wbc_status = wbcRequestResponse(WINBINDD_GETSIDALIASES,
564 &request,
565 &response);
566 BAIL_ON_WBC_ERROR(wbc_status);
568 if (response.data.num_entries &&
569 !response.extra_data.data) {
570 wbc_status = WBC_ERR_INVALID_RESPONSE;
571 goto done;
574 rids = (uint32_t *)wbcAllocateMemory(response.data.num_entries,
575 sizeof(uint32_t), NULL);
576 BAIL_ON_PTR_ERROR(sids, wbc_status);
578 s = (const char *)response.extra_data.data;
579 for (i = 0; i < response.data.num_entries; i++) {
580 char *n = strchr(s, '\n');
581 if (n) {
582 *n = '\0';
584 wbc_status = wbcStringToSid(s, &sid);
585 BAIL_ON_WBC_ERROR(wbc_status);
586 wbc_status = _sid_to_rid(&sid, &rids[i]);
587 BAIL_ON_WBC_ERROR(wbc_status);
588 s += strlen(s) + 1;
591 *num_alias_rids = response.data.num_entries;
592 *alias_rids = rids;
593 rids = NULL;
594 wbc_status = WBC_ERR_SUCCESS;
596 done:
597 wbcFreeMemory(sid_string);
598 free(extra_data);
599 winbindd_free_response(&response);
600 wbcFreeMemory(rids);
601 return wbc_status;
605 /* Lists Users */
606 wbcErr wbcListUsers(const char *domain_name,
607 uint32_t *_num_users,
608 const char ***_users)
610 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
611 struct winbindd_request request;
612 struct winbindd_response response;
613 uint32_t num_users = 0;
614 const char **users = NULL;
615 const char *next;
617 /* Initialise request */
619 ZERO_STRUCT(request);
620 ZERO_STRUCT(response);
622 if (domain_name) {
623 strncpy(request.domain_name, domain_name,
624 sizeof(request.domain_name)-1);
627 wbc_status = wbcRequestResponse(WINBINDD_LIST_USERS,
628 &request,
629 &response);
630 BAIL_ON_WBC_ERROR(wbc_status);
632 users = wbcAllocateStringArray(response.data.num_entries);
633 if (users == NULL) {
634 return WBC_ERR_NO_MEMORY;
637 /* Look through extra data */
639 next = (const char *)response.extra_data.data;
640 while (next) {
641 const char *current;
642 char *k;
644 if (num_users >= response.data.num_entries) {
645 wbc_status = WBC_ERR_INVALID_RESPONSE;
646 goto done;
649 current = next;
650 k = strchr(next, ',');
652 if (k) {
653 k[0] = '\0';
654 next = k+1;
655 } else {
656 next = NULL;
659 users[num_users] = strdup(current);
660 BAIL_ON_PTR_ERROR(users[num_users], wbc_status);
661 num_users += 1;
663 if (num_users != response.data.num_entries) {
664 wbc_status = WBC_ERR_INVALID_RESPONSE;
665 goto done;
668 *_num_users = response.data.num_entries;
669 *_users = users;
670 users = NULL;
671 wbc_status = WBC_ERR_SUCCESS;
673 done:
674 winbindd_free_response(&response);
675 wbcFreeMemory(users);
676 return wbc_status;
679 /* Lists Groups */
680 wbcErr wbcListGroups(const char *domain_name,
681 uint32_t *_num_groups,
682 const char ***_groups)
684 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
685 struct winbindd_request request;
686 struct winbindd_response response;
687 uint32_t num_groups = 0;
688 const char **groups = NULL;
689 const char *next;
691 /* Initialise request */
693 ZERO_STRUCT(request);
694 ZERO_STRUCT(response);
696 if (domain_name) {
697 strncpy(request.domain_name, domain_name,
698 sizeof(request.domain_name)-1);
701 wbc_status = wbcRequestResponse(WINBINDD_LIST_GROUPS,
702 &request,
703 &response);
704 BAIL_ON_WBC_ERROR(wbc_status);
706 groups = wbcAllocateStringArray(response.data.num_entries);
707 if (groups == NULL) {
708 return WBC_ERR_NO_MEMORY;
711 /* Look through extra data */
713 next = (const char *)response.extra_data.data;
714 while (next) {
715 const char *current;
716 char *k;
718 if (num_groups >= response.data.num_entries) {
719 wbc_status = WBC_ERR_INVALID_RESPONSE;
720 goto done;
723 current = next;
724 k = strchr(next, ',');
726 if (k) {
727 k[0] = '\0';
728 next = k+1;
729 } else {
730 next = NULL;
733 groups[num_groups] = strdup(current);
734 BAIL_ON_PTR_ERROR(groups[num_groups], wbc_status);
735 num_groups += 1;
737 if (num_groups != response.data.num_entries) {
738 wbc_status = WBC_ERR_INVALID_RESPONSE;
739 goto done;
742 *_num_groups = response.data.num_entries;
743 *_groups = groups;
744 groups = NULL;
745 wbc_status = WBC_ERR_SUCCESS;
747 done:
748 winbindd_free_response(&response);
749 wbcFreeMemory(groups);
750 return wbc_status;
753 wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
754 char **pdomain,
755 char **pfullname,
756 enum wbcSidType *pname_type)
758 wbcErr wbc_status;
759 char *domain = NULL;
760 char *name = NULL;
761 enum wbcSidType name_type;
763 wbc_status = wbcLookupSid(sid, &domain, &name, &name_type);
764 BAIL_ON_WBC_ERROR(wbc_status);
766 if (name_type == WBC_SID_NAME_USER) {
767 uid_t uid;
768 struct passwd *pwd;
770 wbc_status = wbcSidToUid(sid, &uid);
771 BAIL_ON_WBC_ERROR(wbc_status);
773 wbc_status = wbcGetpwuid(uid, &pwd);
774 BAIL_ON_WBC_ERROR(wbc_status);
776 wbcFreeMemory(name);
778 name = wbcStrDup(pwd->pw_gecos);
779 BAIL_ON_PTR_ERROR(name, wbc_status);
780 wbcFreeMemory(pwd);
783 wbc_status = WBC_ERR_SUCCESS;
785 done:
786 if (WBC_ERROR_IS_OK(wbc_status)) {
787 *pdomain = domain;
788 *pfullname = name;
789 *pname_type = name_type;
790 } else {
791 wbcFreeMemory(domain);
792 wbcFreeMemory(name);
795 return wbc_status;
798 const char* wbcSidTypeString(enum wbcSidType type)
800 switch (type) {
801 case WBC_SID_NAME_USE_NONE: return "SID_NONE";
802 case WBC_SID_NAME_USER: return "SID_USER";
803 case WBC_SID_NAME_DOM_GRP: return "SID_DOM_GROUP";
804 case WBC_SID_NAME_DOMAIN: return "SID_DOMAIN";
805 case WBC_SID_NAME_ALIAS: return "SID_ALIAS";
806 case WBC_SID_NAME_WKN_GRP: return "SID_WKN_GROUP";
807 case WBC_SID_NAME_DELETED: return "SID_DELETED";
808 case WBC_SID_NAME_INVALID: return "SID_INVALID";
809 case WBC_SID_NAME_UNKNOWN: return "SID_UNKNOWN";
810 case WBC_SID_NAME_COMPUTER: return "SID_COMPUTER";
811 default: return "Unknown type";