/*++ * Function: Get_Server_conn * * Purpose: When a client login attempt is made, fetch a usable server * connection descriptor. This means that either we reuse an * existing ICD, or we open a new one. Hide that abstraction from * the caller... * * Parameters: ptr to username string * ptr to password string * const ptr to client hostname or IP string (for logging only) * in_port_t, client port number (for logging only) * unsigned char - flag to indicate that the client sent the * password as a string literal. * * Returns: ICD * on success * NULL on failure * * Authors: Dave McMurtrie <*****@*****.**> * * Credit: Major SSL additions by Ken Murchison <*****@*****.**> * *-- */ extern ICD_Struct *Get_Server_conn( char *Username, char *Password, const char *ClientAddr, in_port_t sin_port, unsigned char LiteralPasswd ) { char *fn = "Get_Server_conn()"; unsigned int HashIndex; ICC_Struct *HashEntry = NULL; char SendBuf[BUFSIZE]; unsigned int BufLen = BUFSIZE - 1; char md5pw[MD5_DIGEST_LENGTH]; char *tokenptr; char *endptr; char *last; ICC_Struct *ICC_Active; ICC_Struct *ICC_tptr; ITD_Struct Server; int rc; unsigned int Expiration; EVP_MD_CTX mdctx; int md_len; Expiration = PC_Struct.cache_expiration_time; memset( &Server, 0, sizeof Server ); /* need to md5 the passwd regardless, so do that now */ EVP_DigestInit(&mdctx, EVP_md5()); EVP_DigestUpdate(&mdctx, Password, strlen(Password)); EVP_DigestFinal(&mdctx, md5pw, &md_len); /* see if we have a reusable connection available */ ICC_Active = NULL; HashIndex = Hash( Username, HASH_TABLE_SIZE ); LockMutex( &mp ); /* * Now we just iterate through the linked list at this hash index until * we either find the string we're looking for or we find a NULL. */ for ( HashEntry = ICC_HashTable[ HashIndex ]; HashEntry; HashEntry = HashEntry->next ) { if ( ( strcmp( Username, HashEntry->username ) == 0 ) && ( HashEntry->logouttime > 1 ) ) { ICC_Active = HashEntry; /* * we found this username in our hash table. Need to know if * the password matches. */ if ( memcmp( md5pw, ICC_Active->hashedpw, sizeof md5pw ) ) { syslog( LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d) because password doesn't match.", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port ); ICC_Active->logouttime = 1; } else { /* * We found a matching password on an inactive server socket. * We can use this guy. Before we release the mutex, set the * logouttime such that we mark this connection as "active" * again. */ ICC_Active->logouttime = 0; /* * The fact that we have this stored in a table as an open * server socket doesn't really mean that it's open. The * server could've closed it on us. * We need a speedy way to make sure this is still open. * We'll set the fd to non-blocking and try to read from it. * If we get a zero back, the connection is closed. If we get * EWOULDBLOCK (or some data) we know it's still open. If we * do read data, make sure we read all the data so we "drain" * any puss that may be left on this socket. */ fcntl( ICC_Active->server_conn->sd, F_SETFL, fcntl( ICC_Active->server_conn->sd, F_GETFL, 0) | O_NONBLOCK ); while ( ( rc = IMAP_Read( ICC_Active->server_conn, Server.ReadBuf, sizeof Server.ReadBuf ) ) > 0 ); if ( !rc ) { syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d). Connection closed by server.", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port ); ICC_Active->logouttime = 1; continue; } if ( errno != EWOULDBLOCK ) { syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s:%d). IMAP_read() error: %s", fn, ICC_Active->server_conn->sd, Username, ClientAddr, sin_port, strerror( errno ) ); ICC_Active->logouttime = 1; continue; } fcntl( ICC_Active->server_conn->sd, F_SETFL, fcntl( ICC_Active->server_conn->sd, F_GETFL, 0) & ~O_NONBLOCK ); /* now release the mutex and return the sd to the caller */ UnLockMutex( &mp ); /* * We're reusing an existing server socket. There are a few * counters we have to deal with. */ IMAPCount->RetainedServerConnections--; IMAPCount->InUseServerConnections++; IMAPCount->TotalServerConnectionsReused++; if ( IMAPCount->InUseServerConnections > IMAPCount->PeakInUseServerConnections ) IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections; syslog(LOG_INFO, "LOGIN: '******' (%s:%d) on existing sd [%d]", Username, ClientAddr, sin_port, ICC_Active->server_conn->sd ); return( ICC_Active->server_conn ); } } } UnLockMutex( &mp ); /* * We don't have an active connection for this user, or the password * didn't match. * Open a connection to the IMAP server so we can attempt to login */ Server.conn = ( ICD_Struct * ) malloc( sizeof ( ICD_Struct ) ); memset( Server.conn, 0, sizeof ( ICD_Struct ) ); Server.conn->sd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( Server.conn->sd == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Unable to open server socket: %s", Username, ClientAddr, sin_port, strerror( errno ) ); goto fail; } if ( PC_Struct.send_tcp_keepalives ) { int onoff = 1; setsockopt( Server.conn->sd, SOL_SOCKET, SO_KEEPALIVE, &onoff, sizeof onoff ); } if ( connect( Server.conn->sd, (struct sockaddr *)&ISD.srv, sizeof(ISD.srv) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Unable to connect to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) ); goto fail; } /* Read & throw away the banner line from the server */ if ( IMAP_Line_Read( &Server ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: No banner line received from IMAP server", Username, ClientAddr, sin_port ); goto fail; } /* * Do STARTTLS if necessary. */ #if HAVE_LIBSSL if ( PC_Struct.login_disabled ) { snprintf( SendBuf, BufLen, "S0001 STARTTLS\r\n" ); if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 ) { syslog(LOG_INFO, "STARTTLS failed: IMAP_Write() failed attempting to send STARTTLS command to IMAP server: %s", strerror( errno ) ); goto fail; } /* * Read the server response */ if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 ) { syslog(LOG_INFO, "STARTTLS failed: No response from IMAP server after sending STARTTLS command" ); goto fail; } /* * Try to match up the tag in the server response to the client tag. */ endptr = Server.ReadBuf + rc; tokenptr = memtok( Server.ReadBuf, endptr, &last ); if ( !tokenptr ) { /* * no tokens found in server response? Not likely, but we still * have to check. */ syslog(LOG_INFO, "STARTTLS failed: server response to STARTTLS command contained no tokens." ); goto fail; } if ( memcmp( (const void *)tokenptr, (const void *)"S0001", strlen( tokenptr ) ) ) { /* * non-matching tag read back from the server... Lord knows what this * is, so we'll fail. */ syslog(LOG_INFO, "STARTTLS failed: server response to STARTTLS command contained non-matching tag." ); goto fail; } /* * Now that we've matched the tags up, see if the response was 'OK' */ tokenptr = memtok( NULL, endptr, &last ); if ( !tokenptr ) { /* again, not likely but we still have to check... */ syslog(LOG_INFO, "STARTTLS failed: Malformed server response to STARTTLS command" ); goto fail; } if ( memcmp( (const void *)tokenptr, "OK", 2 ) ) { /* * If the server sent back a "NO" or "BAD", we can look at the actual * server logs to figure out why. We don't have to break our ass here * putting the string back together just for the sake of logging. */ syslog(LOG_INFO, "STARTTLS failed: non-OK server response to STARTTLS command" ); goto fail; } Server.conn->tls = SSL_new( tls_ctx ); if ( Server.conn->tls == NULL ) { syslog(LOG_INFO, "STARTTLS failed: SSL_new() failed" ); goto fail; } SSL_clear( Server.conn->tls ); rc = SSL_set_fd( Server.conn->tls, Server.conn->sd ); if ( rc == 0 ) { syslog(LOG_INFO, "STARTTLS failed: SSL_set_fd() failed: %d", SSL_get_error( Server.conn->tls, rc ) ); goto fail; } SSL_set_connect_state( Server.conn->tls ); rc = SSL_connect( Server.conn->tls ); if ( rc <= 0 ) { syslog(LOG_INFO, "STARTTLS failed: SSL_connect() failed, %d: %s", SSL_get_error( Server.conn->tls, rc ), SSLerrmessage() ); goto fail; } /* XXX Should we grab the session id for later reuse? */ } #endif /* HAVE_LIBSSL */ /* * Send the login command off to the IMAP server. Have to treat a literal * password different. */ if ( LiteralPasswd ) { snprintf( SendBuf, BufLen, "A0001 LOGIN %s {%d}\r\n", Username, strlen( Password ) ); if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send LOGIN command to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) ); goto fail; } /* * the server response should be a go ahead */ if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Failed to receive go-ahead from IMAP server after sending LOGIN command", Username, ClientAddr, sin_port ); goto fail; } if ( Server.ReadBuf[0] != '+' ) { syslog( LOG_INFO, "LOGIN: '******' (%s:%d) failed: bad response from server after sending string literal specifier", Username, ClientAddr, sin_port ); goto fail; } /* * now send the password */ snprintf( SendBuf, BufLen, "%s\r\n", Password ); if ( IMAP_Write( Server.conn, SendBuf, strlen( SendBuf ) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send literal password to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) ); goto fail; } } else { /* * just send the login command via normal means. */ snprintf( SendBuf, BufLen, "A0001 LOGIN %s %s\r\n", Username, Password ); if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: IMAP_Write() failed attempting to send LOGIN command to IMAP server: %s", Username, ClientAddr, sin_port, strerror( errno ) ); goto fail; } } /* * Read the server response. From RFC 3501: * * A server MAY include a CAPABILITY response code in the tagged OK * response to a successful LOGIN command in order to send * capabilities automatically. It is unnecessary for a client to * send a separate CAPABILITY command if it recognizes these * automatic capabilities. * * We have to be ready for the possibility that this might be an * untagged response... In an ideal world, we'd want to pass the * untagged stuff back to the client. For now, since the RFC doesn't * mandate that behaviour, we're not going to since we don't have a client * socket descriptor to send it to. */ for ( ;; ) { if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: No response from IMAP server after sending LOGIN command", Username, ClientAddr, sin_port ); goto fail; } if ( Server.ReadBuf[0] != '*' ) break; } /* * Try to match up the tag in the server response to the client tag. */ endptr = Server.ReadBuf + rc; tokenptr = memtok( Server.ReadBuf, endptr, &last ); if ( !tokenptr ) { /* * no tokens found in server response? Not likely, but we still * have to check. */ syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: server response to LOGIN command contained no tokens.", Username, ClientAddr, sin_port ); goto fail; } if ( memcmp( (const void *)tokenptr, (const void *)"A0001", strlen( tokenptr ) ) ) { /* * non-matching tag read back from the server... Lord knows what this * is, so we'll fail. */ syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: server response to LOGIN command contained non-matching tag.", Username, ClientAddr, sin_port ); goto fail; } /* * Now that we've matched the tags up, see if the response was 'OK' */ tokenptr = memtok( NULL, endptr, &last ); if ( !tokenptr ) { /* again, not likely but we still have to check... */ syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Malformed server response to LOGIN command", Username, ClientAddr, sin_port ); goto fail; } if ( memcmp( (const void *)tokenptr, "OK", 2 ) ) { /* * If the server sent back a "NO" or "BAD", we can look at the actual * server logs to figure out why. We don't have to break our ass here * putting the string back together just for the sake of logging. */ syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: non-OK server response to LOGIN command", Username, ClientAddr, sin_port ); goto fail; } /* * put this in our used list and remove it from the free list */ for( ; ; ) { LockMutex( &mp ); if ( ICC_free->next ) { /* generate the hash index */ HashIndex = Hash( Username, HASH_TABLE_SIZE ); /* temporarily store the address of the next free structure */ ICC_tptr = ICC_free->next; /* * We want to add the newest "used" structure at the front of * the list at the hash index. */ ICC_free->next = ICC_HashTable[ HashIndex ]; ICC_HashTable[ HashIndex ] = ICC_free; /* * less typing and more readability, set an "active" pointer. */ ICC_Active = ICC_free; /* now point the free listhead to the next available free struct */ ICC_free = ICC_tptr; /* fill in the newest used (oxymoron?) structure */ strncpy( ICC_Active->username, Username, sizeof ICC_Active->username ); memcpy( ICC_Active->hashedpw, md5pw, sizeof ICC_Active->hashedpw ); ICC_Active->logouttime = 0; /* zero means, "it's active". */ ICC_Active->server_conn = Server.conn; UnLockMutex( &mp ); IMAPCount->InUseServerConnections++; IMAPCount->TotalServerConnectionsCreated++; if ( IMAPCount->InUseServerConnections > IMAPCount->PeakInUseServerConnections ) IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections; syslog(LOG_INFO, "LOGIN: '******' (%s:%d) on new sd [%d]", Username, ClientAddr, sin_port, Server.conn->sd ); return( Server.conn ); } /* * There weren't any free ICC structs. Try to free one. Make sure * we unlock the mutex, since ICC_Recycle needs to obtain it. */ UnLockMutex( &mp ); Expiration = abs( Expiration / 2 ); /* * Eventually, we have to fail */ if ( Expiration <= 2 ) { syslog(LOG_INFO, "LOGIN: '******' (%s:%d) failed: Out of free ICC structs.", Username, ClientAddr, sin_port ); goto fail; } ICC_Recycle( Expiration ); } fail: #if HAVE_LIBSSL if ( Server.conn->tls ) { SSL_shutdown( Server.conn->tls ); SSL_free( Server.conn->tls ); } #endif close( Server.conn->sd ); free( Server.conn ); return( NULL ); }
/*++ * Function: Get_Server_sd * * Purpose: When a client login attempt is made, fetch a usable server * socket descriptor. This means that either we reuse an * existing sd, or we open a new one. Hide that abstraction from * the caller... * * Parameters: ptr to username string * ptr to password string * const ptr to client hostname or IP string (for logging only) * * Returns: int sd on success * -1 on failure * * Authors: dgm * *-- */ extern int Get_Server_sd( char *Username, char *Password, const char *ClientAddr ) { char *fn = "Get_Server_sd()"; unsigned int HashIndex; ICC_Struct *HashEntry = NULL; char SendBuf[BUFSIZE]; unsigned int BufLen = BUFSIZE - 1; char md5pw[16]; char *tokenptr; char *endptr; char *last; ICC_Struct *ICC_Active; ICC_Struct *ICC_tptr; ITD_Struct Server; int rc; unsigned int Expiration; Expiration = PC_Struct.cache_expiration_time; memset( &Server, 0, sizeof Server ); /* need to md5 the passwd regardless, so do that now */ md5_calc( md5pw, Password, strlen( Password ) ); /* see if we have a reusable connection available */ ICC_Active = NULL; HashIndex = Hash( Username, HASH_TABLE_SIZE ); LockMutex( &mp ); /* * Now we just iterate through the linked list at this hash index until * we either find the string we're looking for or we find a NULL. */ for ( HashEntry = ICC_HashTable[ HashIndex ]; HashEntry; HashEntry = HashEntry->next ) { if ( ( strcmp( Username, HashEntry->username ) == 0 ) && ( HashEntry->logouttime > 1 ) ) { ICC_Active = HashEntry; /* * we found this username in our hash table. Need to know if * the password matches. */ if ( memcmp( md5pw, ICC_Active->hashedpw, sizeof md5pw ) ) { /* the passwords don't match. Shake this guy. */ UnLockMutex( &mp ); syslog(LOG_INFO, "LOGIN: '******' (%s) failed: password incorrect", Username, ClientAddr ); return( -1 ); } /* * We found a matching password on an inactive server socket. We * can use this guy. Before we release the mutex, set the * logouttime such that we mark this connection as "active" again. */ ICC_Active->logouttime = 0; /* * The fact that we have this stored in a table as an open server * socket doesn't really mean that it's open. The server could've * closed it on us. * We need a speedy way to make sure this is still open. * We'll set the fd to non-blocking and try to read from it. If we * get a zero back, the connection is closed. If we get * EWOULDBLOCK (or some data) we know it's still open. If we do * read data, make sure we read all the data so we "drain" any * puss that may be left on this socket. */ fcntl( ICC_Active->server_sd, F_SETFL, fcntl( ICC_Active->server_sd, F_GETFL, 0) | O_NONBLOCK ); while ( ( rc = recv( ICC_Active->server_sd, Server.ReadBuf, sizeof Server.ReadBuf, 0 ) ) > 0 ); if ( !rc ) { syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s). Connection closed by server.", fn, ICC_Active->server_sd, Username, ClientAddr ); ICC_Active->logouttime = 1; continue; } if ( errno != EWOULDBLOCK ) { syslog(LOG_NOTICE, "%s: Unable to reuse server sd [%d] for user '%s' (%s). recv() error: %s", fn, ICC_Active->server_sd, Username, ClientAddr, strerror( errno ) ); ICC_Active->logouttime = 1; continue; } fcntl( ICC_Active->server_sd, F_SETFL, fcntl( ICC_Active->server_sd, F_GETFL, 0) & ~O_NONBLOCK ); /* now release the mutex and return the sd to the caller */ UnLockMutex( &mp ); /* * We're reusing an existing server socket. There are a few * counters we have to deal with. */ IMAPCount->RetainedServerConnections--; IMAPCount->InUseServerConnections++; IMAPCount->TotalServerConnectionsReused++; if ( IMAPCount->InUseServerConnections > IMAPCount->PeakInUseServerConnections ) IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections; syslog(LOG_INFO, "LOGIN: '******' (%s) on existing sd [%d]", Username, ClientAddr, ICC_Active->server_sd ); return( ICC_Active->server_sd ); } } UnLockMutex( &mp ); /* * We don't have an active connection for this user. * Open a connection to the IMAP server so we can attempt to login */ Server.sd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( Server.sd == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s) failed: Unable to open server socket: %s", Username, ClientAddr, strerror( errno ) ); return( -1 ); } if ( connect( Server.sd, (struct sockaddr *)&ISD.srv, sizeof(ISD.srv) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s) failed: Unable to connect to IMAP server: %s", Username, ClientAddr, strerror( errno ) ); close( Server.sd ); return( -1 ); } /* Read & throw away the banner line from the server */ if ( IMAP_Line_Read( &Server ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s) failed: No banner line received from IMAP server", Username, ClientAddr ); close( Server.sd ); return( -1 ); } /* * Send the login command off to the IMAP server. */ snprintf( SendBuf, BufLen, "A0001 LOGIN %s %s\r\n", Username, Password ); if ( send( Server.sd, SendBuf, strlen(SendBuf), 0 ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s) failed: send() failed attempting to send LOGIN command to IMAP server: %s", Username, ClientAddr, strerror( errno ) ); close( Server.sd ); return( -1 ); } /* * Read the server response */ if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 ) { syslog(LOG_INFO, "LOGIN: '******' (%s) failed: No response from IMAP server after sending LOGIN command", Username, ClientAddr ); close( Server.sd ); return( -1 ); } /* * Try to match up the tag in the server response to the client tag. */ endptr = Server.ReadBuf + rc; tokenptr = memtok( Server.ReadBuf, endptr, &last ); if ( !tokenptr ) { /* * no tokens found in server response? Not likely, but we still * have to check. */ syslog(LOG_INFO, "LOGIN: '******' (%s) failed: server response to LOGIN command contained no tokens.", Username, ClientAddr ); close( Server.sd ); return( -1 ); } if ( memcmp( (const void *)tokenptr, (const void *)"A0001", strlen( tokenptr ) ) ) { /* * non-matching tag read back from the server... Lord knows what this * is, so we'll fail. */ syslog(LOG_INFO, "LOGIN: '******' (%s) failed: server response to LOGIN command contained non-matching tag.", Username, ClientAddr ); close( Server.sd ); return( -1 ); } /* * Now that we've matched the tags up, see if the response was 'OK' */ tokenptr = memtok( NULL, endptr, &last ); if ( !tokenptr ) { /* again, not likely but we still have to check... */ syslog(LOG_INFO, "LOGIN: '******' (%s) failed: Malformed server response to LOGIN command", Username, ClientAddr ); close( Server.sd ); return( -1 ); } if ( memcmp( (const void *)tokenptr, "OK", 2 ) ) { /* * If the server sent back a "NO" or "BAD", we can look at the actual * server logs to figure out why. We don't have to break our ass here * putting the string back together just for the sake of logging. */ syslog(LOG_INFO, "LOGIN: '******' (%s) failed: non-OK server response to LOGIN command", Username, ClientAddr ); close( Server.sd ); return( -1 ); } /* * put this in our used list and remove it from the free list */ for( ; ; ) { LockMutex( &mp ); if ( ICC_free->next ) { /* generate the hash index */ HashIndex = Hash( Username, HASH_TABLE_SIZE ); /* temporarily store the address of the next free structure */ ICC_tptr = ICC_free->next; /* * We want to add the newest "used" structure at the front of * the list at the hash index. */ ICC_free->next = ICC_HashTable[ HashIndex ]; ICC_HashTable[ HashIndex ] = ICC_free; /* * less typing and more readability, set an "active" pointer. */ ICC_Active = ICC_free; /* now point the free listhead to the next available free struct */ ICC_free = ICC_tptr; /* fill in the newest used (oxymoron?) structure */ strncpy( ICC_Active->username, Username, sizeof ICC_Active->username ); memcpy( ICC_Active->hashedpw, md5pw, sizeof ICC_Active->hashedpw ); ICC_Active->logouttime = 0; /* zero means, "it's active". */ ICC_Active->server_sd = Server.sd; UnLockMutex( &mp ); IMAPCount->InUseServerConnections++; IMAPCount->TotalServerConnectionsCreated++; if ( IMAPCount->InUseServerConnections > IMAPCount->PeakInUseServerConnections ) IMAPCount->PeakInUseServerConnections = IMAPCount->InUseServerConnections; syslog(LOG_INFO, "LOGIN: '******' (%s) on new sd [%d]", Username, ClientAddr, Server.sd ); return( Server.sd ); } /* * There weren't any free ICC structs. Try to free one. Make sure * we unlock the mutex, since ICC_Recycle needs to obtain it. */ UnLockMutex( &mp ); Expiration = abs( Expiration / 2 ); /* * Eventually, we have to fail */ if ( Expiration <= 2 ) { syslog(LOG_INFO, "LOGIN: '******' (%s) failed: Out of free ICC structs.", Username, ClientAddr ); close( Server.sd ); return( -1 ); } ICC_Recycle( Expiration ); } }