1 /* $Id: aldap.c,v 1.30 2012/04/30 21:40:03 jmatthew Exp $ */
2 /* $OpenBSD: aldap.c,v 1.30 2012/04/30 21:40:03 jmatthew Exp $ */
6 * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
7 * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 static struct ber_element *ldap_parse_search_filter(struct ber_element *,
37 static struct ber_element *ldap_do_parse_search_filter(
38 struct ber_element *, char **);
39 char **aldap_get_stringset(struct ber_element *);
41 char *parseval(char *, size_t);
42 int aldap_create_page_control(struct ber_element *,
43 int, struct aldap_page_control *);
46 void ldap_debug_elements(struct ber_element *);
50 #define DPRINTF(x...) printf(x)
51 #define LDAP_DEBUG(x, y) do { fprintf(stderr, "*** " x "\n"); ldap_debug_elements(y); } while (0)
53 #define DPRINTF(x...) do { } while (0)
54 #define LDAP_DEBUG(x, y) do { } while (0)
58 aldap_close(struct aldap *al)
60 if (close(al->ber.fd) == -1)
74 if ((a = calloc(1, sizeof(*a))) == NULL)
82 aldap_bind(struct aldap *ldap, char *binddn, char *bindcred)
84 struct ber_element *root = NULL, *elm;
92 if ((root = ber_add_sequence(NULL)) == NULL)
95 elm = ber_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP,
96 (unsigned long)LDAP_REQ_BIND, VERSION, binddn, bindcred,
97 BER_CLASS_CONTEXT, (unsigned long)LDAP_AUTH_SIMPLE);
101 LDAP_DEBUG("aldap_bind", root);
103 error = ber_write_elements(&ldap->ber, root);
104 ber_free_elements(root);
109 return (ldap->msgid);
112 ber_free_elements(root);
114 ldap->err = ALDAP_ERR_OPERATION_FAILED;
119 aldap_unbind(struct aldap *ldap)
121 struct ber_element *root = NULL, *elm;
124 if ((root = ber_add_sequence(NULL)) == NULL)
126 elm = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
131 LDAP_DEBUG("aldap_unbind", root);
133 error = ber_write_elements(&ldap->ber, root);
134 ber_free_elements(root);
139 return (ldap->msgid);
142 ber_free_elements(root);
144 ldap->err = ALDAP_ERR_OPERATION_FAILED;
150 aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter,
151 char **attrs, int typesonly, int sizelimit, int timelimit,
152 struct aldap_page_control *page)
154 struct ber_element *root = NULL, *ber, *c;
157 if ((root = ber_add_sequence(NULL)) == NULL)
160 ber = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP,
161 (unsigned long) LDAP_REQ_SEARCH);
163 ldap->err = ALDAP_ERR_OPERATION_FAILED;
168 ber = ber_printf_elements(ber, "sEEddb", basedn, (long long)scope,
169 (long long)LDAP_DEREF_NEVER, sizelimit,
170 timelimit, typesonly);
172 ldap->err = ALDAP_ERR_OPERATION_FAILED;
176 if ((ber = ldap_parse_search_filter(ber, filter)) == NULL) {
177 ldap->err = ALDAP_ERR_PARSER_ERROR;
181 if ((ber = ber_add_sequence(ber)) == NULL)
184 for (i = 0; attrs[i] != NULL; i++) {
185 if ((ber = ber_add_string(ber, attrs[i])) == NULL)
189 aldap_create_page_control(c, 100, page);
191 LDAP_DEBUG("aldap_search", root);
193 error = ber_write_elements(&ldap->ber, root);
194 ber_free_elements(root);
197 ldap->err = ALDAP_ERR_OPERATION_FAILED;
201 return (ldap->msgid);
205 ber_free_elements(root);
211 aldap_create_page_control(struct ber_element *elm, int size,
212 struct aldap_page_control *page)
216 struct ber_element *ber = NULL;
221 ber = ber_add_sequence(NULL);
224 if (ber_printf_elements(ber, "ds", 50, "") == NULL)
227 if (ber_printf_elements(ber, "dx", 50, page->cookie,
228 page->cookie_len) == NULL)
232 if ((len = ber_write_elements(&c, ber)) < 1)
234 if (ber_printf_elements(elm, "{t{sx", 2, 0, LDAP_PAGED_OID,
235 c.br_wbuf, (size_t)len) == NULL)
238 ber_free_elements(ber);
243 ber_free_elements(ber);
249 struct aldap_message *
250 aldap_parse(struct aldap *ldap)
255 struct aldap_message *m;
256 struct ber_element *a = NULL, *ep;
258 if ((m = calloc(1, sizeof(struct aldap_message))) == NULL)
261 if ((m->msg = ber_read_elements(&ldap->ber, NULL)) == NULL)
264 LDAP_DEBUG("message", m->msg);
266 if (ber_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0)
269 m->message_type = type;
272 switch (m->message_type) {
274 case LDAP_RES_MODIFY:
276 case LDAP_RES_DELETE:
277 case LDAP_RES_MODRDN:
278 case LDAP_RES_COMPARE:
279 case LDAP_RES_SEARCH_RESULT:
280 if (ber_scanf_elements(m->protocol_op, "{EeSeSe",
281 &m->body.res.rescode, &m->dn, &m->body.res.diagmsg, &a) != 0)
283 if (m->body.res.rescode == LDAP_REFERRAL)
284 if (ber_scanf_elements(a, "{e", &m->references) != 0)
286 if (m->msg->be_sub) {
287 for (ep = m->msg->be_sub; ep != NULL; ep = ep->be_next) {
288 ber_scanf_elements(ep, "t", &class, &type);
289 if (class == 2 && type == 0)
290 m->page = aldap_parse_page_control(ep->be_sub->be_sub,
291 ep->be_sub->be_sub->be_len);
296 case LDAP_RES_SEARCH_ENTRY:
297 if (ber_scanf_elements(m->protocol_op, "{eS{e", &m->dn,
298 &m->body.search.attrs) != 0)
301 case LDAP_RES_SEARCH_REFERENCE:
302 if (ber_scanf_elements(m->protocol_op, "{e", &m->references) != 0)
309 ldap->err = ALDAP_ERR_PARSER_ERROR;
314 struct aldap_page_control *
315 aldap_parse_page_control(struct ber_element *control, size_t len)
320 struct ber_element *elm;
321 struct aldap_page_control *page;
325 ber_scanf_elements(control, "ss", &oid, &encoded);
326 ber_set_readbuf(&b, encoded, control->be_next->be_len);
327 elm = ber_read_elements(&b, NULL);
329 if ((page = malloc(sizeof(struct aldap_page_control))) == NULL) {
331 ber_free_elements(elm);
336 ber_scanf_elements(elm->be_sub, "is", &page->size, &s);
337 page->cookie_len = elm->be_sub->be_next->be_len;
339 if ((page->cookie = malloc(page->cookie_len)) == NULL) {
341 ber_free_elements(elm);
346 memcpy(page->cookie, s, page->cookie_len);
348 ber_free_elements(elm);
354 aldap_freepage(struct aldap_page_control *page)
361 aldap_freemsg(struct aldap_message *msg)
364 ber_free_elements(msg->msg);
369 aldap_get_resultcode(struct aldap_message *msg)
371 return msg->body.res.rescode;
375 aldap_get_dn(struct aldap_message *msg)
382 if (ber_get_string(msg->dn, &dn) == -1)
389 aldap_get_references(struct aldap_message *msg)
391 if (msg->references == NULL)
393 return aldap_get_stringset(msg->references);
397 aldap_free_references(char **values)
404 for (i = 0; values[i] != NULL; i++)
411 aldap_get_diagmsg(struct aldap_message *msg)
415 if (msg->body.res.diagmsg == NULL)
418 if (ber_get_string(msg->body.res.diagmsg, &s) == -1)
425 aldap_count_attrs(struct aldap_message *msg)
428 struct ber_element *a;
430 if (msg->body.search.attrs == NULL)
433 for (i = 0, a = msg->body.search.attrs;
434 a != NULL && ber_get_eoc(a) != 0;
442 aldap_first_attr(struct aldap_message *msg, char **outkey, char ***outvalues)
444 struct ber_element *b, *c;
448 if (msg->body.search.attrs == NULL)
451 if (ber_scanf_elements(msg->body.search.attrs, "{s(e)}e",
455 msg->body.search.iter = msg->body.search.attrs->be_next;
457 if ((ret = aldap_get_stringset(b)) == NULL)
461 (*outkey) = utoa(key);
471 aldap_next_attr(struct aldap_message *msg, char **outkey, char ***outvalues)
473 struct ber_element *a, *b;
477 if (msg->body.search.iter == NULL)
480 LDAP_DEBUG("attr", msg->body.search.iter);
482 if (ber_get_eoc(msg->body.search.iter) == 0)
485 if (ber_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b)
489 msg->body.search.iter = msg->body.search.iter->be_next;
491 if ((ret = aldap_get_stringset(a)) == NULL)
495 (*outkey) = utoa(key);
506 aldap_match_attr(struct aldap_message *msg, char *inkey, char ***outvalues)
508 struct ber_element *a, *b;
512 if (msg->body.search.attrs == NULL)
515 LDAP_DEBUG("attr", msg->body.search.attrs);
517 for (a = msg->body.search.attrs;;) {
520 if (ber_get_eoc(a) == 0)
522 if (ber_scanf_elements(a, "{s(e", &descr, &b) != 0)
524 if (strcasecmp(descr, inkey) == 0)
530 if ((ret = aldap_get_stringset(b)) == NULL)
543 aldap_free_attr(char **values)
550 for (i = 0; values[i] != NULL; i++)
560 aldap_free_url(struct aldap_url *lu)
567 aldap_parse_url(char *url, struct aldap_url *lu)
569 char *p, *forward, *forward2;
570 const char *errstr = NULL;
573 if ((lu->buffer = p = strdup(url)) == NULL)
577 if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) != 0)
580 p += strlen(LDAP_URL);
582 /* host and optional port */
583 if ((forward = strchr(p, '/')) != NULL)
585 /* find the optional port */
586 if ((forward2 = strchr(p, ':')) != NULL) {
588 /* if a port is given */
589 if (*(forward2+1) != '\0') {
590 #define PORT_MAX UINT16_MAX
591 lu->port = strtonum(++forward2, 0, PORT_MAX, &errstr);
596 /* fail if no host is given */
602 /* p is assigned either a pointer to a character or to '\0' */
608 if ((forward = strchr(p, '?')) != NULL)
613 /* p is assigned either a pointer to a character or to '\0' */
619 if ((forward = strchr(p, '?')) != NULL)
621 for (i = 0; i < MAXATTR; i++) {
622 if ((forward2 = strchr(p, ',')) == NULL) {
625 lu->attributes[i] = p;
629 lu->attributes[i] = p;
634 /* p is assigned either a pointer to a character or to '\0' */
640 if ((forward = strchr(p, '?')) != NULL)
642 if (strcmp(p, "base") == 0)
643 lu->scope = LDAP_SCOPE_BASE;
644 else if (strcmp(p, "one") == 0)
645 lu->scope = LDAP_SCOPE_ONELEVEL;
646 else if (strcmp(p, "sub") == 0)
647 lu->scope = LDAP_SCOPE_SUBTREE;
669 aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit,
672 struct aldap_url *lu;
674 if ((lu = calloc(1, sizeof(*lu))) == NULL)
677 if (aldap_parse_url(url, lu))
680 if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, lu->attributes,
681 typesonly, sizelimit, timelimit) == -1)
685 return (ldap->msgid);
697 aldap_get_stringset(struct ber_element *elm)
699 struct ber_element *a;
704 if (elm->be_type != BER_TYPE_OCTETSTRING)
707 for (a = elm, i = 1; i > 0 && a != NULL && a->be_type ==
708 BER_TYPE_OCTETSTRING; a = a->be_next, i++)
713 if ((ret = calloc(i + 1, sizeof(char *))) == NULL)
716 for (a = elm, i = 0; a != NULL && a->be_type == BER_TYPE_OCTETSTRING;
717 a = a->be_next, i++) {
719 ber_get_string(a, &s);
728 * Base case for ldap_do_parse_search_filter
731 * struct ber_element *, ber_element tree
734 static struct ber_element *
735 ldap_parse_search_filter(struct ber_element *ber, char *filter)
737 struct ber_element *elm;
742 if (cp == NULL || *cp == '\0') {
747 if ((elm = ldap_do_parse_search_filter(ber, &cp)) == NULL)
751 ber_free_elements(elm);
752 ber_link_elements(ber, NULL);
761 * Translate RFC4515 search filter string into ber_element tree
764 * struct ber_element *, ber_element tree
768 * when cp is passed to a recursive invocation, it is updated
769 * to point one character beyond the filter that was passed
770 * i.e., cp jumps to "(filter)" upon return
772 * goto's used to discriminate error-handling based on error type
773 * doesn't handle extended filters (yet)
776 static struct ber_element *
777 ldap_do_parse_search_filter(struct ber_element *prev, char **cpp)
779 struct ber_element *elm, *root = NULL;
780 char *attr_desc, *attr_val, *parsed_val, *cp;
786 /* cpp should pass in pointer to opening parenthesis of "(filter)" */
795 type = LDAP_FILT_AND;
799 if ((elm = ber_add_set(prev)) == NULL)
802 ber_set_header(elm, BER_CLASS_CONTEXT, type);
804 if (*++cp != '(') /* opening `(` of filter */
809 ldap_do_parse_search_filter(elm, &cp)) == NULL)
813 if (*cp != ')') /* trailing `)` of filter */
818 if ((root = ber_add_sequence(prev)) == NULL)
820 ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT);
822 cp++; /* now points to sub-filter */
823 if ((elm = ldap_do_parse_search_filter(root, &cp)) == NULL)
826 if (*cp != ')') /* trailing `)` of filter */
830 default: /* SIMPLE || PRESENCE */
833 len = strcspn(cp, "()<>~=");
837 type = LDAP_FILT_APPR;
849 type = LDAP_FILT_EQ; /* assume EQ until disproven */
858 /* presence filter */
859 if (strncmp(attr_val, "*)", 2) == 0) {
860 cp++; /* point to trailing `)` */
862 ber_add_nstring(prev, attr_desc, len)) == NULL)
865 ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES);
869 if ((root = ber_add_sequence(prev)) == NULL)
871 ber_set_header(root, BER_CLASS_CONTEXT, type);
873 if ((elm = ber_add_nstring(root, attr_desc, len)) == NULL)
876 len = strcspn(attr_val, "*)");
877 if (len == 0 && *cp != '*')
883 if (*cp == '*') { /* substring filter */
888 ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS);
890 if ((elm = ber_add_sequence(elm)) == NULL)
893 for (initial = 1;; cp++, initial = 0) {
896 len = strcspn(attr_val, "*)");
908 type = LDAP_FILT_SUBS_INIT;
910 type = LDAP_FILT_SUBS_FIN;
912 type = LDAP_FILT_SUBS_ANY;
914 if ((parsed_val = parseval(attr_val, len)) ==
917 elm = ber_add_nstring(elm, parsed_val,
922 ber_set_header(elm, BER_CLASS_CONTEXT, type);
923 if (type == LDAP_FILT_SUBS_FIN)
929 if ((parsed_val = parseval(attr_val, len)) == NULL)
931 elm = ber_add_nstring(elm, parsed_val, strlen(parsed_val));
938 cp++; /* now points one char beyond the trailing `)` */
943 syntaxfail: /* XXX -- error reporting */
947 ber_free_elements(root);
948 ber_link_elements(prev, NULL);
954 * Display a list of ber elements.
958 ldap_debug_elements(struct ber_element *root)
960 static int indent = 0;
969 /* calculate lengths */
972 switch (root->be_encoding) {
973 case BER_TYPE_SEQUENCE:
975 constructed = root->be_encoding;
982 fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
983 switch (root->be_class) {
984 case BER_CLASS_UNIVERSAL:
985 fprintf(stderr, "class: universal(%u) type: ", root->be_class);
986 switch (root->be_type) {
988 fprintf(stderr, "end-of-content");
990 case BER_TYPE_BOOLEAN:
991 fprintf(stderr, "boolean");
993 case BER_TYPE_INTEGER:
994 fprintf(stderr, "integer");
996 case BER_TYPE_BITSTRING:
997 fprintf(stderr, "bit-string");
999 case BER_TYPE_OCTETSTRING:
1000 fprintf(stderr, "octet-string");
1003 fprintf(stderr, "null");
1005 case BER_TYPE_OBJECT:
1006 fprintf(stderr, "object");
1008 case BER_TYPE_ENUMERATED:
1009 fprintf(stderr, "enumerated");
1011 case BER_TYPE_SEQUENCE:
1012 fprintf(stderr, "sequence");
1015 fprintf(stderr, "set");
1019 case BER_CLASS_APPLICATION:
1020 fprintf(stderr, "class: application(%u) type: ",
1022 switch (root->be_type) {
1024 fprintf(stderr, "bind");
1027 fprintf(stderr, "bind");
1029 case LDAP_REQ_UNBIND_30:
1031 case LDAP_REQ_SEARCH:
1032 fprintf(stderr, "search");
1034 case LDAP_RES_SEARCH_ENTRY:
1035 fprintf(stderr, "search_entry");
1037 case LDAP_RES_SEARCH_RESULT:
1038 fprintf(stderr, "search_result");
1040 case LDAP_REQ_MODIFY:
1041 fprintf(stderr, "modify");
1043 case LDAP_RES_MODIFY:
1044 fprintf(stderr, "modify");
1047 fprintf(stderr, "add");
1050 fprintf(stderr, "add");
1052 case LDAP_REQ_DELETE_30:
1053 fprintf(stderr, "delete");
1055 case LDAP_RES_DELETE:
1056 fprintf(stderr, "delete");
1058 case LDAP_REQ_MODRDN:
1059 fprintf(stderr, "modrdn");
1061 case LDAP_RES_MODRDN:
1062 fprintf(stderr, "modrdn");
1064 case LDAP_REQ_COMPARE:
1065 fprintf(stderr, "compare");
1067 case LDAP_RES_COMPARE:
1068 fprintf(stderr, "compare");
1070 case LDAP_REQ_ABANDON_30:
1071 fprintf(stderr, "abandon");
1075 case BER_CLASS_PRIVATE:
1076 fprintf(stderr, "class: private(%u) type: ", root->be_class);
1077 fprintf(stderr, "encoding (%lu) type: ", root->be_encoding);
1079 case BER_CLASS_CONTEXT:
1080 /* XXX: this is not correct */
1081 fprintf(stderr, "class: context(%u) type: ", root->be_class);
1082 switch(root->be_type) {
1083 case LDAP_AUTH_SIMPLE:
1084 fprintf(stderr, "auth simple");
1089 fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
1092 fprintf(stderr, "(%lu) encoding %lu ",
1093 root->be_type, root->be_encoding);
1096 root->be_encoding = constructed;
1098 switch (root->be_encoding) {
1099 case BER_TYPE_BOOLEAN:
1100 if (ber_get_boolean(root, &d) == -1) {
1101 fprintf(stderr, "<INVALID>\n");
1104 fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d);
1106 case BER_TYPE_INTEGER:
1107 if (ber_get_integer(root, &v) == -1) {
1108 fprintf(stderr, "<INVALID>\n");
1111 fprintf(stderr, "value %lld\n", v);
1113 case BER_TYPE_ENUMERATED:
1114 if (ber_get_enumerated(root, &v) == -1) {
1115 fprintf(stderr, "<INVALID>\n");
1118 fprintf(stderr, "value %lld\n", v);
1120 case BER_TYPE_BITSTRING:
1121 if (ber_get_bitstring(root, (void *)&buf, &len) == -1) {
1122 fprintf(stderr, "<INVALID>\n");
1125 fprintf(stderr, "hexdump ");
1126 for (i = 0; i < len; i++)
1127 fprintf(stderr, "%02x", buf[i]);
1128 fprintf(stderr, "\n");
1130 case BER_TYPE_OBJECT:
1131 if (ber_get_oid(root, &o) == -1) {
1132 fprintf(stderr, "<INVALID>\n");
1135 fprintf(stderr, "\n");
1137 case BER_TYPE_OCTETSTRING:
1138 if (ber_get_nstring(root, (void *)&buf, &len) == -1) {
1139 fprintf(stderr, "<INVALID>\n");
1142 fprintf(stderr, "string \"%.*s\"\n", len, buf);
1144 case BER_TYPE_NULL: /* no payload */
1146 case BER_TYPE_SEQUENCE:
1149 fprintf(stderr, "\n");
1153 if (constructed && root->be_sub) {
1155 ldap_debug_elements(root->be_sub);
1159 ldap_debug_elements(root->be_next);
1164 * Convert UTF-8 to ASCII.
1166 * non-ASCII characters are displayed as '?'
1167 * the argument u should be a NULL terminated sequence of UTF-8 bytes.
1175 /* calculate the length to allocate */
1176 for (len = 0, i = 0; u[i] != '\0'; ) {
1177 if ((u[i] & 0xF0) == 0xF0)
1179 else if ((u[i] & 0xE0) == 0xE0)
1181 else if ((u[i] & 0xC0) == 0xC0)
1188 if ((str = calloc(len + 1, sizeof(char))) == NULL)
1191 /* copy the ASCII characters to the newly allocated string */
1192 for (i = 0, j = 0; u[i] != '\0'; j++) {
1193 if ((u[i] & 0xF0) == 0xF0) {
1196 } else if ((u[i] & 0xE0) == 0xE0) {
1199 } else if ((u[i] & 0xC0) == 0xC0) {
1212 * Parse a LDAP value
1214 * the argument u should be a NULL terminated sequence of ASCII bytes.
1217 parseval(char *p, size_t len)
1220 char *cp = p, *buffer, *newbuffer;
1221 size_t size, newsize, i, j;
1224 if ((buffer = calloc(1, size)) == NULL)
1227 for (i = j = 0; j < len; i++) {
1229 newsize = size + 1024;
1230 if ((newbuffer = realloc(buffer, newsize)) == NULL) {
1238 if (cp[j] == '\\') {
1239 strlcpy(hex, cp + j + 1, sizeof(hex));
1240 buffer[i] = (char)strtoumax(hex, NULL, 16);
1252 aldap_get_errno(struct aldap *a, const char **estr)
1255 case ALDAP_ERR_SUCCESS:
1258 case ALDAP_ERR_PARSER_ERROR:
1259 *estr = "parser failed";
1261 case ALDAP_ERR_INVALID_FILTER:
1262 *estr = "invalid filter";
1264 case ALDAP_ERR_OPERATION_FAILED:
1265 *estr = "operation failed";