/* The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-) Copyright (C) 2001,2002,2003,2004,2005 Aymeric MOIZARD jack@atosc.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "parser.h" int osip_www_authenticate_init (osip_www_authenticate_t ** dest) { *dest = (osip_www_authenticate_t *) osip_malloc (sizeof (osip_www_authenticate_t)); if (*dest == NULL) return -1; memset (*dest, 0, sizeof (osip_www_authenticate_t)); return 0; } /* fills the www-authenticate header of message. */ /* INPUT : char *hvalue | value of header. */ /* OUTPUT: osip_message_t *sip | structure to save results. */ /* returns -1 on error. */ int osip_message_set_www_authenticate (osip_message_t * sip, const char *hvalue) { osip_www_authenticate_t *www_authenticate; int i; if (hvalue == NULL || hvalue[0] == '\0') return 0; if (sip == NULL) return -1; i = osip_www_authenticate_init (&www_authenticate); if (i != 0) return -1; i = osip_www_authenticate_parse (www_authenticate, hvalue); if (i != 0) { osip_www_authenticate_free (www_authenticate); return -1; } sip->message_property = 2; osip_list_add (&sip->www_authenticates, www_authenticate, -1); return 0; } int __osip_quoted_string_set (const char *name, const char *str, char **result, const char **next) { *next = str; if (*result != NULL) return 0; /* already parsed */ *next = NULL; while ((' ' == *str) || ('\t' == *str) || (',' == *str)) if (*str) str++; else return -1; /* bad header format */ if (strlen (str) <= strlen (name)) return -1; /* bad header format... */ if (osip_strncasecmp (name, str, strlen (name)) == 0) { const char *quote1; const char *quote2; const char *tmp; const char *hack = strchr (str, '='); if (hack == NULL) return -1; while (' ' == *(hack - 1)) /* get rid of extra spaces */ hack--; if ((size_t) (hack - str) != strlen (name)) { *next = str; return 0; } quote1 = __osip_quote_find (str); if (quote1 == NULL) return -1; /* bad header format... */ quote2 = __osip_quote_find (quote1 + 1); if (quote2 == NULL) return -1; /* bad header format... */ if (quote2 - quote1 == 1) { /* this is a special case! The quote contains nothing! */ /* example: Digest opaque="",cnonce="" */ /* in this case, we just forget the parameter... this */ /* this should prevent from user manipulating empty */ /* strings */ tmp = quote2 + 1; /* next element start here */ for (; *tmp == ' ' || *tmp == '\t'; tmp++) { } for (; *tmp == '\n' || *tmp == '\r'; tmp++) { } /* skip LWS */ *next = NULL; if (*tmp == '\0') /* end of header detected */ return 0; if (*tmp != '\t' && *tmp != ' ') /* LWS here ? */ *next = tmp; else { /* it is: skip it... */ for (; *tmp == ' ' || *tmp == '\t'; tmp++) { } if (*tmp == '\0') /* end of header detected */ return 0; *next = tmp; } return 0; } *result = (char *) osip_malloc (quote2 - quote1 + 3); if (*result == NULL) return -1; osip_strncpy (*result, quote1, quote2 - quote1 + 1); tmp = quote2 + 1; /* next element start here */ for (; *tmp == ' ' || *tmp == '\t'; tmp++) { } for (; *tmp == '\n' || *tmp == '\r'; tmp++) { } /* skip LWS */ *next = NULL; if (*tmp == '\0') /* end of header detected */ return 0; if (*tmp != '\t' && *tmp != ' ') /* LWS here ? */ *next = tmp; else { /* it is: skip it... */ for (; *tmp == ' ' || *tmp == '\t'; tmp++) { } if (*tmp == '\0') /* end of header detected */ return 0; *next = tmp; } } else *next = str; /* wrong header asked! */ return 0; } int __osip_token_set (const char *name, const char *str, char **result, const char **next) { const char *beg; const char *tmp; *next = str; if (*result != NULL) return 0; /* already parsed */ *next = NULL; beg = strchr (str, '='); if (beg == NULL) return -1; /* bad header format... */ if (strlen (str) < 6) return 0; /* end of header... */ while ((' ' == *str) || ('\t' == *str) || (',' == *str)) if (*str) str++; else return -1; /* bad header format */ if (osip_strncasecmp (name, str, strlen (name)) == 0) { const char *end; end = strchr (str, ','); if (end == NULL) end = str + strlen (str); /* This is the end of the header */ if (end - beg < 2) return -1; *result = (char *) osip_malloc (end - beg); if (*result == NULL) return -1; osip_clrncpy (*result, beg + 1, end - beg - 1); /* make sure the element does not contain more parameter */ tmp = (*end) ? (end + 1) : end; for (; *tmp == ' ' || *tmp == '\t'; tmp++) { } for (; *tmp == '\n' || *tmp == '\r'; tmp++) { } /* skip LWS */ *next = NULL; if (*tmp == '\0') /* end of header detected */ return 0; if (*tmp != '\t' && *tmp != ' ') /* LWS here ? */ *next = tmp; else { /* it is: skip it... */ for (; *tmp == ' ' || *tmp == '\t'; tmp++) { } if (*tmp == '\0') /* end of header detected */ return 0; *next = tmp; } } else *next = str; /* next element start here */ return 0; } /* fills the www-authenticate strucuture. */ /* INPUT : char *hvalue | value of header. */ /* OUTPUT: osip_message_t *sip | structure to save results. */ /* returns -1 on error. */ /* TODO: digest-challenge tken has no order preference?? verify many situations (extra SP....) */ int osip_www_authenticate_parse (osip_www_authenticate_t * wwwa, const char *hvalue) { const char *space; const char *next = NULL; space = strchr (hvalue, ' '); /* SEARCH FOR SPACE */ if (space == NULL) return -1; if (space - hvalue + 1 < 2) return -1; wwwa->auth_type = (char *) osip_malloc (space - hvalue + 1); if (wwwa->auth_type == NULL) return -1; osip_strncpy (wwwa->auth_type, hvalue, space - hvalue); for (;;) { int parse_ok = 0; if (__osip_quoted_string_set ("realm", space, &(wwwa->realm), &next)) return -1; if (next == NULL) return 0; /* end of header detected! */ else if (next != space) { space = next; parse_ok++; } if (__osip_quoted_string_set ("domain", space, &(wwwa->domain), &next)) return -1; if (next == NULL) return 0; /* end of header detected! */ else if (next != space) { space = next; parse_ok++; } if (__osip_quoted_string_set ("nonce", space, &(wwwa->nonce), &next)) return -1; if (next == NULL) return 0; /* end of header detected! */ else if (next != space) { space = next; parse_ok++; } if (__osip_quoted_string_set ("opaque", space, &(wwwa->opaque), &next)) return -1; if (next == NULL) return 0; /* end of header detected! */ else if (next != space) { space = next; parse_ok++; } if (__osip_token_set ("stale", space, &(wwwa->stale), &next)) return -1; if (next == NULL) return 0; /* end of header detected! */ else if (next != space) { space = next; parse_ok++; } if (__osip_token_set ("algorithm", space, &(wwwa->algorithm), &next)) return -1; if (next == NULL) return 0; /* end of header detected! */ else if (next != space) { space = next; parse_ok++; } if (__osip_quoted_string_set ("qop", space, &(wwwa->qop_options), &next)) return -1; if (next == NULL) return 0; /* end of header detected! */ else if (next != space) { space = next; parse_ok++; } if (0 == parse_ok) { char *quote1, *quote2, *tmp; /* CAUTION */ /* parameter not understood!!! I'm too lazy to handle IT */ /* let's simply bypass it */ if (strlen (space) < 1) return 0; tmp = strchr (space + 1, ','); if (tmp == NULL) /* it was the last header */ return 0; quote1 = __osip_quote_find (space); if ((quote1 != NULL) && (quote1 < tmp)) /* this may be a quoted string! */ { quote2 = __osip_quote_find (quote1 + 1); if (quote2 == NULL) return -1; /* bad header format... */ if (tmp < quote2) /* the comma is inside the quotes! */ space = strchr (quote2, ','); else space = tmp; if (space == NULL) /* it was the last header */ return 0; } else space = tmp; /* continue parsing... */ } } return 0; /* ok */ } /* returns the www_authenticate header. */ /* INPUT : osip_message_t *sip | sip message. */ /* returns null on error. */ int osip_message_get_www_authenticate (const osip_message_t * sip, int pos, osip_www_authenticate_t ** dest) { osip_www_authenticate_t *www_authenticate; *dest = NULL; if (osip_list_size (&sip->www_authenticates) <= pos) return -1; /* does not exist */ www_authenticate = (osip_www_authenticate_t *) osip_list_get (&sip->www_authenticates, pos); *dest = www_authenticate; return pos; } char * osip_www_authenticate_get_auth_type (osip_www_authenticate_t * www_authenticate) { return www_authenticate->auth_type; } void osip_www_authenticate_set_auth_type (osip_www_authenticate_t * www_authenticate, char *auth_type) { www_authenticate->auth_type = (char *) auth_type; } char * osip_www_authenticate_get_realm (osip_www_authenticate_t * www_authenticate) { return www_authenticate->realm; } void osip_www_authenticate_set_realm (osip_www_authenticate_t * www_authenticate, char *realm) { www_authenticate->realm = (char *) realm; } char * osip_www_authenticate_get_domain (osip_www_authenticate_t * www_authenticate) { return www_authenticate->domain; } void osip_www_authenticate_set_domain (osip_www_authenticate_t * www_authenticate, char *domain) { www_authenticate->domain = (char *) domain; } char * osip_www_authenticate_get_nonce (osip_www_authenticate_t * www_authenticate) { return www_authenticate->nonce; } void osip_www_authenticate_set_nonce (osip_www_authenticate_t * www_authenticate, char *nonce) { www_authenticate->nonce = (char *) nonce; } char * osip_www_authenticate_get_stale (osip_www_authenticate_t * www_authenticate) { return www_authenticate->stale; } void osip_www_authenticate_set_stale (osip_www_authenticate_t * www_authenticate, char *stale) { www_authenticate->stale = (char *) stale; } char * osip_www_authenticate_get_opaque (osip_www_authenticate_t * www_authenticate) { return www_authenticate->opaque; } void osip_www_authenticate_set_opaque (osip_www_authenticate_t * www_authenticate, char *opaque) { www_authenticate->opaque = (char *) opaque; } char * osip_www_authenticate_get_algorithm (osip_www_authenticate_t * www_authenticate) { return www_authenticate->algorithm; } void osip_www_authenticate_set_algorithm (osip_www_authenticate_t * www_authenticate, char *algorithm) { www_authenticate->algorithm = (char *) algorithm; } char * osip_www_authenticate_get_qop_options (osip_www_authenticate_t * www_authenticate) { return www_authenticate->qop_options; } void osip_www_authenticate_set_qop_options (osip_www_authenticate_t * www_authenticate, char *qop_options) { www_authenticate->qop_options = (char *) qop_options; } /* returns the www_authenticate header as a string. */ /* INPUT : osip_www_authenticate_t *www_authenticate | www_authenticate header. */ /* returns null on error. */ int osip_www_authenticate_to_str (const osip_www_authenticate_t * wwwa, char **dest) { size_t len; char *tmp; *dest = NULL; if ((wwwa == NULL) || (wwwa->auth_type == NULL)) return -1; len = strlen (wwwa->auth_type) + 1; if (wwwa->realm != NULL) len = len + strlen (wwwa->realm) + 7; if (wwwa->nonce != NULL) len = len + strlen (wwwa->nonce) + 8; len = len + 2; if (wwwa->domain != NULL) len = len + strlen (wwwa->domain) + 9; if (wwwa->opaque != NULL) len = len + strlen (wwwa->opaque) + 9; if (wwwa->stale != NULL) len = len + strlen (wwwa->stale) + 8; if (wwwa->algorithm != NULL) len = len + strlen (wwwa->algorithm) + 12; if (wwwa->qop_options != NULL) len = len + strlen (wwwa->qop_options) + 6; tmp = (char *) osip_malloc (len); if (tmp == NULL) return -1; *dest = tmp; tmp = osip_str_append (tmp, wwwa->auth_type); if (wwwa->realm != NULL) { tmp = osip_strn_append (tmp, " realm=", 7); tmp = osip_str_append (tmp, wwwa->realm); } if (wwwa->domain != NULL) { tmp = osip_strn_append (tmp, ", domain=", 9); tmp = osip_str_append (tmp, wwwa->domain); } if (wwwa->nonce != NULL) { tmp = osip_strn_append (tmp, ", nonce=", 8); tmp = osip_str_append (tmp, wwwa->nonce); } if (wwwa->opaque != NULL) { tmp = osip_strn_append (tmp, ", opaque=", 9); tmp = osip_str_append (tmp, wwwa->opaque); } if (wwwa->stale != NULL) { tmp = osip_strn_append (tmp, ", stale=", 8); tmp = osip_str_append (tmp, wwwa->stale); } if (wwwa->algorithm != NULL) { tmp = osip_strn_append (tmp, ", algorithm=", 12); tmp = osip_str_append (tmp, wwwa->algorithm); } if (wwwa->qop_options != NULL) { tmp = osip_strn_append (tmp, ", qop=", 6); tmp = osip_str_append (tmp, wwwa->qop_options); } if (wwwa->realm == NULL) { /* remove comma */ len = strlen (wwwa->auth_type); if ((*dest)[len] == ',') (*dest)[len] = ' '; } return 0; } /* deallocates a osip_www_authenticate_t structure. */ /* INPUT : osip_www_authenticate_t *www_authenticate | www_authenticate. */ void osip_www_authenticate_free (osip_www_authenticate_t * www_authenticate) { if (www_authenticate == NULL) return; osip_free (www_authenticate->auth_type); osip_free (www_authenticate->realm); osip_free (www_authenticate->domain); osip_free (www_authenticate->nonce); osip_free (www_authenticate->opaque); osip_free (www_authenticate->stale); osip_free (www_authenticate->algorithm); osip_free (www_authenticate->qop_options); osip_free (www_authenticate); } int osip_www_authenticate_clone (const osip_www_authenticate_t * wwwa, osip_www_authenticate_t ** dest) { int i; osip_www_authenticate_t *wa; *dest = NULL; if (wwwa == NULL) return -1; if (wwwa->auth_type == NULL) return -1; i = osip_www_authenticate_init (&wa); if (i == -1) /* allocation failed */ return -1; wa->auth_type = osip_strdup (wwwa->auth_type); if (wwwa->realm != NULL) wa->realm = osip_strdup (wwwa->realm); if (wwwa->domain != NULL) wa->domain = osip_strdup (wwwa->domain); if (wwwa->nonce != NULL) wa->nonce = osip_strdup (wwwa->nonce); if (wwwa->opaque != NULL) wa->opaque = osip_strdup (wwwa->opaque); if (wwwa->stale != NULL) wa->stale = osip_strdup (wwwa->stale); if (wwwa->algorithm != NULL) wa->algorithm = osip_strdup (wwwa->algorithm); if (wwwa->qop_options != NULL) wa->qop_options = osip_strdup (wwwa->qop_options); *dest = wa; return 0; }